Skip to main content
Smarthome 8 mins

Monitoring websites and web servers with Node-Red

Website monitoring doesn’t have to be left to an external service, but can simply be done with Node-Red - including convenient notification.

Monitoring websites and web servers with Node-Red
Table of Contents

Node-Red is my new universal tool for all kinds of automation problems and tasks. So why not use it to implement monitoring for the web server and your own website, which informs you via Telegram and email in case of an error, i.e. website down or without content?

Apart from the Telegram nodes, which you have to install additionally, my flow relies on the standard nodes of the basic installation of Node-Red.

⚠ [affiliate] Keine Produkte mit Cache für home-assistant. Bitte affiliate-sync --lang en ausführen.

Regularly knocking on the server’s door

It starts with an Injection Node, which triggers the check at a 5-minute interval. A completely sufficient period for my requirements.

This triggers an HTTP Request Node, in which the desired URL is entered and called up via a GET command. A UTF-8 string is then returned.

From here, the flow splits into two branches. The first branch checks the Response Code of the page. If the website returns Response Code 200, everything is okay: page and server are online.

The Switch Node triggers when the response code is not equal to 200.

A response doesn’t yet mean content

However, this doesn’t yet guarantee that there is actually something to see on the page. If you have, for example, an Nginx server as a reverse proxy in use, it can return 200 as a response, but the page can still be empty because the server behind the proxy isn’t delivering any data. Therefore, I check in a second branch whether a certain content is contained on the page.

An HTML Node searches for an element on the website. I use the word Markus for this, which is found in the copyright footer of fricklr.com. This tells me that the page was completely rendered. The word is contained in the HTML element div.copyright-bar, which you can simply find with the “Inspect” function of Chrome.

If the text is found in the HTML element, its entire text content is returned as Payload. The subsequent Switch Node checks with a regular expression, which can be extremely simple here, whether Markus is also contained in the text.

Since the notification should only trigger if the searched word is NOT contained, you add a second output to the node with otherwise, which then triggers the subsequent nodes.

Notifying, but not annoying

Now you could attach a corresponding action, notification, etc. to each branch. However, in case of an error, this action or message would be sent every 5 minutes, since the Injection Node at the beginning executes the flow every 5 minutes. Therefore, I have inserted a Delay Node in each branch.

With the Rate Limit action, you can set how often a message should be forwarded. In case of an error, I want to get another reminder every 30 minutes. All messages in between are simply discarded:

The notification nodes are the same in both branches, apart from the message.

Alarm via Telegram and email

A notification is sent via Telegram. For this, a Telegram Bot must be set up and the ID of the recipient(s) must be specified. How to do this is described in the description of the Telegram Node .

Additionally, I send an email via the SMTP server of Gmail. For this, the subject and the content of the email must first be defined with the Function Node:

javascript
1
2
3
4
return {
    payload: `[ALARM] ${msg.responseUrl} Homepage shows an empty page`,
    topic: 'ALARM: fricklr ERROR!'
}

The email content is in the Payload, the subject in the Topic. This Function Node is also the same in both branches, only the message differs of course.

The variable msg.responseURL is practical if you want to monitor several URLs at once, because the monitored URL is automatically included in the message.

Both Function Nodes now end at the Email Node. The full Gmail address is entered as the Userid.

Sending is done via the SMTP server of Gmail. For this, “Access for less secure apps” must be activated in the settings of Gmail under Security, otherwise you cannot use SMTP for external applications.

This completes the flow for website and web server monitoring. The easiest way to test the flow is to set the status code in the corresponding Switch Node to a different value or to enter a text in the text branch that doesn’t exist in the defined element. You should now receive an email and Telegram message with the alarm.

Conclusion

At this point, you can now also trigger various other functions. For example, you could also pass the alarm via MQTT MQTTLightweight publish/subscribe messaging protocol. Used in smart homes to exchange sensor data and control commands between devices to an ESPHome ESPHomeFramework for configuring ESP32/ESP8266 microcontrollers that automatically integrate into Home Assistant. Ideal for DIY sensors and actuators or ESPeasy, which then makes an LED blink or a speaker beep. Passing it on to the smart home is also practical.

In any case, it’s great how you can build a highly practical and free solution for monitoring the website and the web server with just a few nodes.

And here is the complete flow for import into Node-Red:

javascript
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
[
    {
        "id": "dbd92bb5.58bb68",
        "type": "tab",
        "label": "Website Monitor",
        "disabled": false,
        "info": ""
    },
    {
        "id": "3ab27a41.60baa6",
        "type": "inject",
        "z": "dbd92bb5.58bb68",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "300",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 470,
        "y": 580,
        "wires": [
            [
                "6f88489b.7f3f88"
            ]
        ]
    },
    {
        "id": "6f88489b.7f3f88",
        "type": "http request",
        "z": "dbd92bb5.58bb68",
        "name": "YourURL.com",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "https://yourURL",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 670,
        "y": 580,
        "wires": [
            [
                "fe359cc8.2f164",
                "75d0dac2.a64df4"
            ]
        ]
    },
    {
        "id": "fe359cc8.2f164",
        "type": "switch",
        "z": "dbd92bb5.58bb68",
        "name": "200 Status",
        "property": "statusCode",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "200",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 1050,
        "y": 580,
        "wires": [
            [
                "cea5cfe4.de6c1"
            ]
        ]
    },
    {
        "id": "6957b0.d583785",
        "type": "switch",
        "z": "dbd92bb5.58bb68",
        "name": "Test content",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "regex",
                "v": "Markus",
                "vt": "str",
                "case": true
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 1050,
        "y": 480,
        "wires": [
            [],
            [
                "23ee8eb3.661a32"
            ]
        ]
    },
    {
        "id": "75d0dac2.a64df4",
        "type": "html",
        "z": "dbd92bb5.58bb68",
        "name": "Search text",
        "property": "payload",
        "outproperty": "payload",
        "tag": "div.copyright-bar",
        "ret": "text",
        "as": "multi",
        "x": 880,
        "y": 480,
        "wires": [
            [
                "6957b0.d583785"
            ]
        ]
    },
    {
        "id": "326cbee7.91dbe2",
        "type": "telegrambot-notify",
        "z": "dbd92bb5.58bb68",
        "name": "Status Website EMPTY",
        "bot": "8334d252.8f3f8",
        "chatId": "1234567",
        "message": "<b>ALARM:  fricklr *EMPTY CONTENT*</b> <a href=\"https://fricklr.com\">LINK</a>",
        "parseMode": "HTML",
        "x": 1540,
        "y": 400,
        "wires": []
    },
    {
        "id": "e3b816ab.b04088",
        "type": "telegrambot-notify",
        "z": "dbd92bb5.58bb68",
        "name": "Status Website DOWN",
        "bot": "8334d252.8f3f8",
        "chatId": "474756997",
        "message": "<b>ALARM:  fricklr *DOWN*</b> <a href=\"https://fricklr.com\">LINK</a>",
        "parseMode": "HTML",
        "x": 1540,
        "y": 660,
        "wires": []
    },
    {
        "id": "f40e9f3.0caec6",
        "type": "e-mail",
        "z": "dbd92bb5.58bb68",
        "server": "smtp.gmail.com",
        "port": "465",
        "secure": true,
        "tls": false,
        "name": "your-email",
        "dname": "Gmail",
        "x": 1710,
        "y": 520,
        "wires": []
    },
    {
        "id": "beb279ff.93bbe8",
        "type": "function",
        "z": "dbd92bb5.58bb68",
        "name": "Mail Server down",
        "func": "\nreturn {\n    payload: `[ALARM] ${msg.responseUrl} responds with status code '${msg.statusCode}'`,\n    topic: 'ALARM: fricklr DOWN!'\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 1530,
        "y": 580,
        "wires": [
            [
                "f40e9f3.0caec6"
            ]
        ]
    },
    {
        "id": "ea587cd5.eb755",
        "type": "function",
        "z": "dbd92bb5.58bb68",
        "name": "Mail Page empty",
        "func": "\nreturn {\n    payload: `[ALARM] ${msg.responseUrl} Homepage shows an empty page`,\n    topic: 'ALARM: fricklr ERROR!'\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 1520,
        "y": 480,
        "wires": [
            [
                "f40e9f3.0caec6"
            ]
        ]
    },
    {
        "id": "cea5cfe4.de6c1",
        "type": "delay",
        "z": "dbd92bb5.58bb68",
        "name": "Wait 30 minutes",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "30",
        "rateUnits": "minute",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "x": 1250,
        "y": 580,
        "wires": [
            [
                "beb279ff.93bbe8",
                "e3b816ab.b04088"
            ]
        ]
    },
    {
        "id": "23ee8eb3.661a32",
        "type": "delay",
        "z": "dbd92bb5.58bb68",
        "name": "Wait 30 minutes",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "30",
        "rateUnits": "minute",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "x": 1250,
        "y": 480,
        "wires": [
            [
                "ea587cd5.eb755",
                "326cbee7.91dbe2"
            ]
        ]
    },
    {
        "id": "8334d252.8f3f8",
        "type": "telegrambot-config",
        "z": "",
        "botname": "Your Bot",
        "usernames": "",
        "chatIds": "1234567",
        "pollInterval": "1800"
    }
]

Hi! I'm Markus – the Fricklr. ⚡ Electrical engineer, 🎵 musician (bass, guitar, keys) and professionally 'something with the internet' 🌐 for over 30 years – nowadays with AI too 🤖 – a 55-year-old generalist from Bavaria, Germany.

Your feedback helps me write even more interesting posts. After "Yes" there's also a small option to support my work.

Explore related topics

Content Map →
Show related content as a list
💡 Drag, hover & click to explore
Bigger dots are more relevant

What do you think? Leave a comment!

Share your thoughts, questions or experiences with the community.

Links marked with this symbol are affiliate links. As an Amazon Associate I earn from qualifying purchases. There are no additional costs for you.