Documentation · Integration Guide
Server Cron Setup
This document explains why a real server cron job is the recommended way to drive imports for the ADP Car Market Hub plugin in production, how it relates to the built-in WP-Cron scheduler, and which operational checks should be in place once it is running.
When to use this document
Use this document if you are:
- Preparing a production deployment and deciding how scheduled imports should be triggered.
- Migrating from WP-Cron to a server-side cron job because imports are unreliable on a low-traffic site.
- Operating a host that recommends or requires
DISABLE_WP_CRONfor performance reasons. - Reviewing why imports start late, run only during peak hours, or do not start at all.
The audience is a WordPress administrator with access to the server's cron facility (for example via cPanel, Plesk, the hosting control panel, or shell access to a Linux crontab).
Overview
WordPress ships with a built-in scheduler called WP-Cron. WP-Cron does not run continuously in the background — it is checked when somebody requests a page on the site. On a busy site this is usually fine, but it has a few well-known limitations:
- On a low-traffic site, scheduled tasks run late or not at all because no visitor triggers WP-Cron.
- WP-Cron runs inside a normal HTTP request, so it shares the request's PHP memory limit and execution time.
- Several visitors arriving at the same time can race to run the same scheduled task, which is wasteful even if the plugin guards against double-runs internally.
For these reasons, the recommended production setup is to disable WP-Cron and trigger it from a real server cron job. The plugin provides a dedicated, token-secured REST endpoint specifically for this purpose, so that the cron job triggers an import directly without depending on a visitor.
The plugin offers two cron modes that you choose on CMH Center → Import & Limits (the Automation card):
- WP-Cron mode (default). The plugin's WP-Cron schedule (hourly, every 6 hours, twice daily, daily, or custom with a 15-minute minimum) drives imports. Suitable for development, evaluation and small sites with regular traffic.
- Server cron mode. The WP-Cron schedule is disabled and the import is triggered by a server cron job that calls the plugin's REST endpoint. Recommended for production.
In both modes, the actual import work is performed by the same shared runner inside the plugin — the only difference is what triggers it.
This one central schedule drives every connected data source. The live sources (AutoScout24, mobile.de and carcuro) have no interval of their own; they run on this schedule. The feed and e-mail sources, configured on CMH Center → Connections, additionally have their own interval. See the Connections Reference.
Why server cron is recommended in production
- Reliable timing. A server cron job runs on a fixed schedule, regardless of whether anybody is visiting the website at that moment.
- No request-time penalty. Imports do not slow down a real visitor's page load, because they run in their own PHP process.
- Predictable resource usage. A server cron job runs once per scheduled time, instead of being kicked off opportunistically by each visitor.
- Easier monitoring. Cron runs leave a clear audit trail (in the cron service's logs and in the plugin's logs) and can be paired with external uptime checks.
- Compatible with hosts that throttle or disable WP-Cron. Some managed hosts limit how often
wp-cron.phpruns internally; an external trigger bypasses that.
Server cron also pairs well with the plugin's image queue: a second cron job that calls wp-cron.php at a short interval keeps WordPress's normal task queue (including image processing) running on a predictable cadence.
Prerequisites
Before switching to server cron, confirm:
- The plugin is installed, activated and the API connection works. See API Credentials Setup and Connection Test.
- The hosting environment provides access to the server's cron facility (cPanel cron jobs, Plesk scheduled tasks, hosting-control-panel cron, or a Linux crontab via SSH).
- The site can be reached over HTTPS by the cron job. For most hosts the cron command runs on the same server and can use
curlto call back into the site. - You are able to edit
wp-config.php(this is required to setDISABLE_WP_CRON). - The general hosting and cron / background processing requirements are met.
Step by step instructions
The plugin's Automation card shows the exact REST trigger URL and example commands for the current installation. Use those values rather than copying URLs from documentation.
- Switch the plugin to server cron mode. Open CMH Center → Import & Limits. In the cron mode card, select Server cron and save. The plugin will hide the WP-Cron schedule controls and show a Server Cron Setup card with a clean REST Trigger URL (no secret in the URL), a separate Authentication header block, and example commands.
- Disable the WordPress built-in cron. Add the following line to
wp-config.php, anywhere above the/* That's all, stop editing! */comment:
define( 'DISABLE_WP_CRON', true );
Without this line, both WP-Cron and the server cron job will try to schedule imports, which is wasteful even when the plugin's internal lock prevents double runs.
- Copy the REST trigger URL and the authentication header. The Server Cron Setup card displays the trigger URL in the form
https://<your-site>/wp-json/as24ci/v1/cron-import(no secret — safe to keep in scripts and logs) and, separately, theAuthorization: Bearer <token>header that carries the secret. Use the Copy buttons so both values are copied verbatim. The token is shown only once (for about 15 minutes) after you generate or regenerate it; afterwards only a one-way hash is stored, so save it into your cron job before leaving the page. Treat the token as a secret — anyone who has it can trigger an import. - Add the import cron job. In your server's cron facility, create a job that calls the trigger URL on the cadence you want, sending the token in the
Authorizationheader. The plugin pre-fills an example like:
*/15 * * * * curl -s -H "Authorization: Bearer <secret>" "https://<your-site>/wp-json/as24ci/v1/cron-import" > /dev/null
Adjust the schedule expression (*/15 * * * * means every 15 minutes) to fit the dealer's needs. Imports skip unchanged vehicles thanks to change detection, so frequent runs are typically inexpensive.
- Add a second cron job to keep WordPress's task queue running. Because
DISABLE_WP_CRONalso disables the queue used by the plugin's image worker and by other WordPress features, add a second cron job that runswp-cron.phpdirectly on a short interval. The plugin's example uses the absolute path of the WordPress installation:
*/5 * * * * php /path/to/wordpress/wp-cron.php > /dev/null 2>&1
The path is shown in the Automation card based on the current WordPress installation. Replace it with the value displayed there.
- Verify with a manual run. From a workstation, call the REST trigger URL once with
curl, sending the token in the header:
curl -s -H "Authorization: Bearer <secret>" "https://<your-site>/wp-json/as24ci/v1/cron-import"
A successful run returns a JSON response describing the import result. A 403 with "Invalid or missing token" means the header or token was copied incorrectly.
- Verify on the next scheduled run. Wait for the next scheduled cron tick, then open CMH Center → System & Help and confirm that Last external cron run shows a recent timestamp.
Legacy query-string authentication (deprecated)
For backward compatibility the endpoint still accepts the token as a query parameter, in the form …/wp-json/as24ci/v1/cron-import?token=<secret>:
*/15 * * * * curl -s "https://<your-site>/wp-json/as24ci/v1/cron-import?token=<secret>" > /dev/null
This method is deprecated because the secret travels in the URL, where it is recorded in server, proxy and CDN access logs (and visible in process listings). Existing cron jobs that use it keep working, but prefer the Authorization: Bearer header for new setups and migrate older jobs when convenient.
Configuration reference
The settings that affect server cron mode are on CMH Center → Import & Limits (Automation card).
| Setting | Purpose |
|---|---|
| Cron mode | Switches between WP-Cron and Server cron. In Server cron mode the WP-Cron schedule is disabled and the Server Cron Setup card is shown. |
| REST trigger URL | The URL the server cron job should call: …/wp-json/as24ci/v1/cron-import. It contains no secret, so it is safe to keep in scripts and logs. Generated from the current site URL. |
| Authentication header | The Authorization: Bearer <token> header that carries the secret to the endpoint. The token is shown only once (about 15 minutes) after generate/regenerate; only a one-way hash is kept at rest. Use the Regenerate Token button to issue a new one (existing cron jobs must then be updated with the new header). |
| Example cron commands | Pre-filled example commands using the current trigger URL and the absolute path to wp-cron.php. Use the Copy buttons to copy them verbatim. |
| Max vehicles per cron run | Limits how many vehicles a single import run will process. 0 means unlimited. Useful as a safety cap on shared hosts. |
| Image queue mode | When enabled, scheduled imports download only the first image per vehicle immediately and queue the rest for asynchronous processing. Recommended in production. |
Operational notes
- One scheduler at a time. Do not run WP-Cron and server cron mode in parallel. Switching to Server cron in the plugin disables the WP-Cron schedule; adding
DISABLE_WP_CRONtowp-config.phpcompletes the picture by stopping WordPress from running its cron queue from front-end requests. - The shared runner. The REST endpoint, the WP-Cron hook and the Trigger now admin button all delegate to the same import runner inside the plugin. There is no separate "server cron import path" with different behaviour.
- Concurrency safety. The runner uses an internal transient-based lock to prevent two imports from running at the same time. Even if a cron job overlaps, the second run is short-circuited.
- Token confidentiality. The cron token grants the right to trigger an import. Treat it as a secret, and send it in the
Authorization: Bearerheader rather than the URL so it never lands in access logs. Avoid pasting the header (or a legacytoken=...URL) into screenshots or tickets; redact the secret before sharing. - Token rotation. Use Regenerate Token whenever you suspect the token is exposed, or as part of a periodic rotation. After regeneration, update every cron job that uses the URL — the previous URL will be rejected.
- Endpoint location. The REST endpoint is the standard WordPress REST URL
…/wp-json/as24ci/v1/cron-import. If the WordPress site URL changes (HTTPS migration, domain change, multi-site move), update the cron job accordingly. - External heartbeat. Any request to the site that includes
?as24ci_cron=1records a timestamp the System & Help tab uses to confirm that an external scheduler is alive. This is purely informational and does not by itself trigger an import. - Image queue companion job. When images are queued during scheduled imports, the queue is processed by the WordPress task queue. If you disabled WP-Cron, the second cron job that calls
wp-cron.phpis what keeps that queue moving. - Do not rely on browser-based testing. Pasting the trigger URL into a browser works for verification, but should not be used as the actual trigger — that depends on someone keeping a browser open.
Operational checks after setup
Run through these checks once after switching to server cron mode, and again after any major change (host migration, URL change, token regeneration):
- Cron service is enabled. Confirm that the host's cron facility is active and that the user account running the job has permission to execute the command.
- Scheduled command is correct. The schedule expression matches the desired frequency, the URL matches the value shown on the Automation card, and the second job that calls
wp-cron.phpexists. DISABLE_WP_CRONis defined. The System & Help tab confirms the constant is set when you are in server cron mode.- Last external cron run is recent. The System & Help tab shows a Last external cron run timestamp within the expected interval.
- Last import run is recent. The dashboard widget and System & Help tab show recent activity from the importer.
- Logs do not show repeated authentication failures. A pattern of 403 responses in the logs means the cron job is using a stale or wrong token.
- Image queue is draining. The Import & Limits tab shows that the image queue is being processed (or is empty).
- Optional external monitor. Configure an uptime monitor or hosting-level alert that notifies you if the import job stops running.
Verify these checks against the current plugin version before publishing customer-facing instructions, since UI labels can evolve between releases.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| Cron job runs but the response is 403 with "Invalid or missing token". | The token in the Authorization header (or legacy token= parameter) is wrong, contains whitespace, or was regenerated after the cron job was created. | Re-copy the authentication header from the Server Cron Setup card and update the cron command. |
| Cron job runs but the response is 403 with "Cron token not configured". | The token has not been generated yet. | Open the Automation card to auto-generate a token, then copy the new URL. |
| Cron job runs successfully, but the System & Help tab still shows no recent external run. | The cron service is calling a different host (for example HTTP instead of HTTPS, or www. vs the canonical hostname) and the request is being redirected away from WordPress. | Use the exact URL shown on the Automation card. Confirm the host's cron service can reach the site over HTTPS. |
| Imports are running, but image attachments stop appearing. | DISABLE_WP_CRON is set but no cron job calls wp-cron.php, so the image queue worker never runs. | Add the second cron job for wp-cron.php as described above. |
| Imports run too long or hit a memory limit. | The host's PHP CLI limits are stricter than expected. | Reduce Max vehicles per cron run, keep image queue mode enabled, and review Cron and Background Processing. |
| Imports run twice in quick succession. | Both WP-Cron mode and a server cron job are active. | Switch the plugin to Server cron mode and add DISABLE_WP_CRON to wp-config.php. |
| Cron job runs but always returns "Another import is already running". | A previous run did not finish (long-running batch, host-side timeout). The transient lock has not yet expired. | Wait for the lock to expire, then investigate the previous run via the plugin logs. See Cron Errors. |