Skip to content

n8n: check a site every hour and log whether it is up

Checked 10 November 2026 · n8n 2.46.7 · Node.js 24.22.1 · 10 min read

A workflow that requests a site once an hour and appends one line per check to a log file — OK when the site answers 200, FAIL with the real status code when it does not. The point is the log: a check that only records the good hours is not a log. The workflow file below is the exact file that was imported and run for this guide.

The workflow at a glance

Node versions in the file: Schedule Trigger 1.4, HTTP Request 4.5, IF 2.4, Code 2, Convert to File 1.2, Read/Write Files 1.1.

Key result: every hour, the workflow requests https://example.com and appends one line to uptime.csv. In the test run, three scheduled runs in a row each added one OK line, and a run against a page that does not exist added one FAIL line with status 404. The log held 7 lines at the end: the header plus 6 checks.

Download the workflow: workflow.json — import it into n8n in step 3. It is the exact file that was imported and run for this guide, including the file path used on the test machine (change it in step 4).

What you need

  • Node.js 20.19 to 24.x. n8n requires a Node.js version between 20.19 and 24.x, inclusive (n8n Docs, Install with npm). This guide ran on Node.js 24.22.1.
  • n8n 2.46.7, started on your own machine. No account, no credentials — the check is a plain GET request to a public site.
  • A terminal for the install and the file setup.
  • A site to check. This guide checks https://example.com. Swap in your own site by changing the URL in the HTTP Request node — the log lines take the URL from that node, so they always name the site that was actually checked.

1. Install and start n8n

Run n8n from the terminal. n8n Docs, Install with npm describes this as the quick way to try n8n locally: the command downloads what it needs and serves the editor at http://localhost:5678.

Command

npx n8n

Open http://localhost:5678. The first start asks you to create an owner account (an email address and a password, kept on your own machine). Note that npm-based installs are deprecated from n8n 3.0, per the same install page — if you are on 3.0 or later, use the Docker install it links to instead, and re-check the node versions above before importing.

2. Make the file

On a default n8n 2 install, the Read/Write Files node can only write under ~/.n8n-files. That is the default value of N8N_RESTRICT_FILE_ACCESS_TO from n8n 2.0 (n8n Docs, Read/Write Files from Disk). Create the folder and the file with its header row now — the workflow appends data rows only, so the header has to exist first.

Command

mkdir -p ~/.n8n-files
printf 'checked_at,url,status,result\n' > ~/.n8n-files/uptime.csv

3. Import the workflow

In n8n, open the workflow menu (the ⋯ at the top right of the canvas) and choose Import from file, then pick workflow.json. The canvas shows nine nodes: three in a line, then the IF node splitting into two branches of three. What each node does:

NodeTypeJob
Every hourSchedule TriggerRuns the workflow at a fixed time. It works like the Cron utility: this workflow uses the custom cron expression 0 * * * *, hourly on the hour (n8n Docs, Schedule Trigger). You must publish the workflow for the schedule to fire. Note the timezone: the node uses the workflow timezone if set, otherwise the instance timezone, which defaults to America/New_York for self-hosted instances — set the workflow timezone in the workflow settings if the hour should mean your time.
Check example.comHTTP RequestRequests https://example.com with method GET (n8n Docs, HTTP Request). Three settings in Options → Response make the check work: Include Response Headers and Status on, so the next node can read the status code; Never Error on, so a 404 or 500 is passed on instead of failing the node; and Response Format set to Text, since the check is the status code, not the page. With those settings the node outputs statusCode, headers, and body. Leave Never Error off and a down site errors the whole run instead of logging a FAIL line — that is the first trap below.
Is it up?IFSplits the workflow on one Number condition: {{ $json.statusCode }} is equal to 200 (n8n Docs, If). A 200 takes the true branch; anything else takes the false branch.

The two Code nodes build the log lines, one per branch, in Run Once for All Items mode (n8n Docs, Code). The URL in each line comes from the HTTP Request node's own parameters, so the line always names the site that was actually checked:

Config — Build OK line, JavaScript

// One CSV line for a site that answered 200.
// checked_at is the run time in ISO format; the URL comes from the
// HTTP Request node's own parameters, so the line always names the
// site that was actually checked.
const item = $input.first().json;
const esc = (v) => {
  const s = String(v ?? '');
  return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
};
const url = $('Check example.com').params.url;
const line = [$now.toISO(), url, item.statusCode, 'OK'].map(esc).join(',');
return [{ json: { line: line + '\n' } }];

Config — Build FAIL line, JavaScript

// One CSV line for a site that did not answer 200.
// The status is the real code the site returned, so a 404 and a 500
// do not look the same in the log.
const item = $input.first().json;
const esc = (v) => {
  const s = String(v ?? '');
  return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
};
const url = $('Check example.com').params.url;
const line = [$now.toISO(), url, item.statusCode, 'FAIL'].map(esc).join(',');
return [{ json: { line: line + '\n' } }];
NodeTypeJob
To text file / To text file (FAIL)Convert to FileOperation Convert to Text File, source field line, with Add BOM off (n8n Docs, Convert to File). One node per branch — n8n needs distinct node names, so the false branch has its own copy.
Append OK / Append FAILRead/Write FilesOperation Write File to Disk, with the Append option on (n8n Docs, Read/Write Files from Disk). Both branches append to the same file, so the log holds OK and FAIL lines in time order.

4. Set the file path

Open the Append OK and Append FAIL nodes and set File Path and Name to the absolute path of the file you made in step 2 — for example /home/alex/.n8n-files/uptime.csv. The downloaded file carries the path used on the test machine (/home/hatch/.n8n-files/uptime.csv); change it to yours in both nodes. Keep the file under ~/.n8n-files unless you have changed N8N_RESTRICT_FILE_ACCESS_TO, or the write is refused.

5. Activate it

Click Publish. The schedule only fires on a published workflow — the Schedule Trigger docs say so directly (n8n Docs, Schedule Trigger). The first run lands at the next hour.

6. Prove the FAIL branch works

Do not wait for the site to go down to find out whether the FAIL branch works. Point the check at a page that does not exist, run it once by hand, then put the URL back:

  1. Open the Check example.com node and change the URL to https://example.com/this-page-does-not-exist-12345.
  2. Click Execute workflow in the editor. The run should finish with status success — a 404 is not an error here, because Never Error is on.
  3. Change the URL back to https://example.com and save.

In the test run for this guide, that manual run appended 2026-11-10T10:12:56.600-05:00,https://example.com/this-page-does-not-exist-12345,404,FAIL to the log. The status in the line is the real 404, and the URL in the line is the page that was actually requested — that is why the Code nodes read the URL from the HTTP Request node instead of hard-coding it.

7. Prove the schedule works

Do not wait an hour to find out whether the schedule fires. Temporarily change the schedule to run every minute, watch the runs, then set it back:

  1. Open the Every hour node, switch the trigger interval to Custom (Cron), and set the expression to */1 * * * *.
  2. Publish the workflow again and wait about three minutes.
  3. Open the Executions tab. You should see three scheduled runs, all with status success.
  4. Set the expression back to 0 * * * * and publish again.

In the test run for this guide, three scheduled runs fired at 15:14, 15:15, and 15:16 UTC, all status success. Each one appended one OK line to the log.

8. Check the file

Command

cat ~/.n8n-files/uptime.csv

Output (the whole file from the test run)

checked_at,url,status,result
2026-11-10T10:11:06.753-05:00,https://example.com,200,OK
2026-11-10T10:12:56.600-05:00,https://example.com/this-page-does-not-exist-12345,404,FAIL
2026-11-10T10:14:00.717-05:00,https://example.com,200,OK
2026-11-10T10:15:00.715-05:00,https://example.com,200,OK
2026-11-10T10:16:00.817-05:00,https://example.com,200,OK
2026-11-10T10:16:14.622-05:00,https://example.com,200,OK

The file held 7 lines after the test: the header plus 6 checks — 5 OK and 1 FAIL. The timestamps carry the instance timezone offset (-05:00 here), not UTC, because $now.toISO() uses the instance timezone. The last line is from a final manual run after the schedule was restored to hourly, to confirm the shipped file still logs an OK. Run wc -l ~/.n8n-files/uptime.csv after the next scheduled run — the count should grow by one every hour.

Key result: six runs, six lines. Three manual runs (OK, FAIL, OK) and three scheduled runs (OK, OK, OK), all status success. The FAIL line carries the real 404 and the URL that produced it. The schedule was restored to 0 * * * * and the workflow left published.

Traps

TRAPa 404 fails the node by default. The HTTP Request node returns success only on a 2xx code unless Never Error is on. Without it, the first down site errors the run and nothing is logged — the exact case this workflow exists to record. Turn on Never Error and Include Response Headers and Status together, as in step 3.
TRAPthe status code is not in the output by default. With the default settings the node returns only the body. The IF node has nothing to compare unless Include Response Headers and Status is on, which adds statusCode to the output. The docs say the same — the node returns only the body unless that option is turned on.
TRAPtwo nodes cannot share a name. The true and false branches each need a Convert to File node and a Read/Write Files node, and n8n routes by node name. The second copies are named To text file (FAIL) and Append FAIL in the workflow file; giving both branches the same name breaks the wiring.
TRAPthe header row must already exist. The workflow appends data rows only. If uptime.csv does not exist, the first run creates it without a header, and the file has no column names. Make the file with the header first, as in step 2.
TRAPthe hour means the instance timezone unless you set one. The Schedule Trigger uses the workflow timezone if set, otherwise the instance timezone, which defaults to America/New_York for self-hosted instances. The log timestamps in step 8 carry a -05:00 offset for the same reason. If the workflow fires at the wrong hour, set the timezone in the workflow settings.
TRAPn8n was stopped when this guide was built. The test machine's n8n had received a SIGTERM and was not listening on port 5678, even though it was expected to be running. It was restarted with the same user folder (N8N_USER_FOLDER pointing at the existing n8n-home), which kept both existing workflows and the owner account intact. If the API answers with a connection error instead of a response, check the process before assuming the workflow is wrong.

End state

A Schedule Trigger fires hourly, an HTTP Request fetches the site with the status code exposed and errors suppressed, an IF node sends 200s down the true branch and everything else down the false branch, and each branch builds one CSV line and appends it to the same file. Six runs logged six lines: five OK, one FAIL with a real 404. The two mistakes that cost time during the build — the HTTP Request node failing on a 404 instead of logging it, and the status code being absent from the output until the full-response option was turned on — are both in the traps above, with the working settings in the workflow file.

Sources

Verified 10 November 2026 on n8n 2.46.7 (Node.js 24.22.1) by importing workflow.json and running it: manual execution 17 (15:11:05 UTC, status success) appended an OK line; manual execution 21 (15:12:55 UTC, status success) against https://example.com/this-page-does-not-exist-12345 appended a FAIL line with status 404; three scheduled executions (24, 27, 29 at 15:14, 15:15, and 15:16 UTC, all status success) each appended one OK line; a final manual execution 30 (15:16:13 UTC, status success) appended one more OK line after the schedule was restored to 0 * * * *. The log held 7 lines at the end: the header plus 6 checks. The workflow was left published on the hourly schedule.