Why Security Monitoring Missed an Incident You Found Yourself
For customers who noticed suspicious activity or abuse before their security monitoring did. This runbook helps you confirm whether the event was out of scope, misconfigured, suppressed, or never reaching the monitoring pipeline, then apply concrete fixes and add coverage gaps to your checks.
TL;DR — You noticed something suspicious yourself, but your security monitoring never alerted. In most cases, the event either was never being collected, the alert rule was too narrow, or notifications were muted or routed to the wrong place. Start by checking whether the raw event exists in your logging/security dashboard at all; that single check usually tells you whether this is a collection problem or an alerting problem. Reading time: ~6 min
The scenario
It is a normal Tuesday afternoon and you spot something odd before any alert arrives: a burst of failed logins from one country you do not do business in, a new admin user you did not create, or a sudden spike in requests to /wp-login.php on a non-WordPress site. Your site is still up, customers are not complaining yet, and your monitoring dashboard still looks green. You open your inbox and chat notifications expecting a security alert, but there is nothing there. Now you need to answer two questions quickly: did the monitoring miss the event entirely, or did it see it and fail to notify anyone?
Symptoms
- You observed suspicious activity manually, but no security alert was sent by email, chat, pager, or webhook.
- The application, CDN (content delivery network), or server logs contain events like:
401 Unauthorized
403 Forbidden
POST /login 401
Failed password for invalid user admin from 203.0.113.24
Accepted password for deploy from 198.51.100.10
New user created: role=admin email=unknown@example.com
- Your web access logs show spikes such as:
203.0.113.24 - - [30/Oct/2026:14:03:11 +0000] "POST /login HTTP/1.1" 401 612
203.0.113.24 - - [30/Oct/2026:14:03:13 +0000] "POST /login HTTP/1.1" 401 612
203.0.113.24 - - [30/Oct/2026:14:03:15 +0000] "POST /login HTTP/1.1" 200 1248
- Your identity provider (company login system) audit log shows events, but your XDR/SIEM (security event platform) does not.
- The security dashboard shows "No incidents" or "No detections" for the time window where you can clearly see the activity elsewhere.
- A test alert from the monitoring tool works, but the real event did not trigger one.
Likely causes
| Cause | How common | Quick check |
|---|---|---|
| The event source was never being collected into the security platform | Very common | In your security dashboard, search the exact IP, username, or timestamp from the suspicious event |
| An alert rule exists, but its conditions are too narrow or filtered to the wrong environment | Very common | Open your provider's dashboard → Security/Detections/Rules and review the rule query for the affected service/environment |
| Notifications were disabled, muted, or routed to an old email/chat/webhook | Common | Open your provider's dashboard → Notifications/Integrations and send a test notification |
| The event was intentionally suppressed/deduplicated as "known noise" | Common | Open your provider's dashboard → Alerts/Incidents and filter for "suppressed", "muted", or "correlated" |
| Time range, timezone, or clock drift hid the event during review | Sometimes | Re-run the search using a 24-hour window around the event time |
| A parser/integration change broke field mapping, so the rule no longer matched | Sometimes | Search raw logs for the event and compare field names like source.ip, client_ip, username, event.action |
Step-by-step diagnosis
- Search for the raw event in the security dashboard
In your provider's dashboard, open the log/security search page and search for one exact indicator you already know: the source IP, username, or a 5-minute time window around the event. If you have command-line access to your log store, use a simple grep on exported logs:
grep -E "203.0.113.24|unknown@example.com|POST /login" app.log auth.log access.log
This is your problem if: the event appears in app/server logs but not in the security platform.
Jump to: ### Event source was never being collected
- If the raw event exists, check whether a rule should have matched it
In your provider's dashboard, go to Security/Detections/Rules and open the rule for failed logins, admin creation, impossible travel, or whichever behavior fits the incident. Look for environment filters likeproductiononly, status filters like>=500only, or thresholds like "10 failures in 1 minute" when you only had 5 in 10 minutes.
{"example_rule":"event.action=login_failed AND count >= 10 AND window = 1m AND env = production"}
This is your problem if: the event is visible in logs, but the rule clearly excludes it by service, environment, status code, path, threshold, or field name.
Jump to: ### Alert rule too narrow or filtered wrong
- Test the notification path
In your provider's dashboard, open Notifications/Integrations and use the built-in "Send test" action for email, Slack, Teams, PagerDuty, or webhook. If you manage a webhook endpoint yourself, verify recent deliveries in your endpoint logs.
curl -i -X POST https://example-webhook-endpoint.invalid/security-test \
-H 'Content-Type: application/json' \
-d '{"test":"security notification"}'
This is your problem if: the rule would have matched, but the test notification fails, goes to an old channel, or is muted outside business hours.
Jump to: ### Notifications disabled or routed wrong
- Check whether the alert was suppressed or merged into another incident
In your provider's dashboard, open Alerts/Incidents and filter for suppressed, muted, correlated, deduplicated, or low-priority events for the same time period. Some systems intentionally reduce noise by grouping repeated events.
Filter: time=last 24h, source IP=203.0.113.24, status in (suppressed, correlated, muted)
This is your problem if: you find the event, but it was auto-closed, merged into another incident, or suppressed by a noise-reduction rule.
Jump to: ### Alert was suppressed or deduplicated
- Expand the time window and verify timestamps
Search again using a full 24-hour window around the event and compare the timestamp in the app log with the timestamp shown in the security platform. Look for UTC (Coordinated Universal Time) versus local time differences.
date -u
This is your problem if: the event exists, but only appears hours earlier/later than expected, or your review window missed it.
Jump to: ### Time range or clock mismatch
- Compare raw fields with what the rule expects
If your platform lets you view raw JSON, inspect one event and compare field names to the rule. A parser change can turnsource.ipintoclient.ip, orevent.action=login_failedinto plain text inmessage.
{"timestamp":"2026-10-30T14:03:15Z","client.ip":"203.0.113.24","message":"Failed password for invalid user admin","service":"web"}
This is your problem if: the event is present, but the fields used by the alert rule are missing or renamed.
Jump to: ### Parser or field mapping changed
Fixes
Event source was never being collected
Add the missing log source to your security platform. In your hosting or cloud dashboard, enable log export for the affected source first: web server access logs, auth logs, CDN logs, identity provider audit logs, or database audit logs. Then connect that source to your security tool.
If you manage an nginx server yourself, confirm logs are being written:
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
Then ship them with your log agent (example Fluent Bit):
[INPUT]
Name tail
Path /var/log/nginx/access.log,/var/log/auth.log
Tag app.logs
Read_from_Head true
Verify it worked: search the security dashboard for a fresh request from your own IP and confirm it appears within a few minutes.
Alert rule too narrow or filtered wrong
Broaden the rule so it matches the real event pattern you saw. Common fixes are lowering the threshold, expanding the time window, adding the missing environment, or matching the actual field names.
Example rule adjustment:
{
"name": "Repeated failed logins",
"query": "event.action=login_failed AND service=web AND env IN (production, staging)",
"threshold": {"count": 5, "window": "10m", "group_by": ["source.ip"]}
}
If your logs do not have event.action, match the message instead:
{"query":"message CONTAINS \"Failed password\" OR (http.path=\"/login\" AND http.status=401)"}
Verify it worked: run the rule in preview mode or against the last 24 hours and confirm it matches the incident you already found.
Notifications disabled or routed wrong
Update recipients and test every destination. In your provider's dashboard, open Notifications/Integrations and replace old email addresses, expired webhooks, or archived chat channels.
For a webhook you control, return a success status:
{"status":"ok"}
For email/chat, send a test and confirm delivery to the current on-call destination.
Verify it worked: the provider's "Send test" action arrives in the correct inbox/channel within a minute.
Alert was suppressed or deduplicated
Adjust suppression rules so they reduce noise without hiding important events. Keep deduplication for identical repeats, but do not suppress first-seen admin creation, successful login after many failures, or new source countries.
Example suppression logic to remove:
{"suppress_if":"message CONTAINS 'login'", "duration":"24h"}
Safer version:
{"suppress_if":"event.action=login_failed AND count < 3", "duration":"15m"}
Verify it worked: re-run the past event through rule preview and confirm it now creates an incident instead of a suppressed record.
Time range or clock mismatch
Set all systems to UTC where possible and widen dashboard defaults. In your app, server, and log shipper settings, use UTC timestamps. If you control Linux hosts:
timedatectl set-timezone UTC
timedatectl status
If you cannot change host time, at least set your dashboard saved searches to "Last 24 hours" and note the timezone in the search.
Verify it worked: the same event shows the same timestamp across app logs, server logs, and the security dashboard.
Parser or field mapping changed
Update the parser or the rule to use the fields your logs actually contain. If your platform supports custom parsing, map the IP, username, action, and status fields explicitly.
Example normalized event shape:
{
"@timestamp": "2026-10-30T14:03:15Z",
"event.action": "login_failed",
"source.ip": "203.0.113.24",
"user.name": "admin",
"http.response.status_code": 401,
"service.name": "web"
}
If normalization is not available, rewrite the rule against raw fields:
{"query":"client.ip:* AND message CONTAINS 'Failed password'"}
Verify it worked: one fresh event now shows the expected fields, and the rule preview matches it.
Prevention
- Add a "pipeline health" alert for missing logs, not just bad logs. Alert if a source goes quiet unexpectedly.
{"name":"No web auth logs received","query":"service=web AND log_type=auth","threshold":{"count":0,"window":"15m"}}
- Create a canary security event weekly. For example, trigger one known failed login from an approved IP and confirm an alert is created.
⚠️ Do this only against a test account you control. Do not test against production admin accounts.
curl -i -X POST https://your-site.example/login \
-H 'Content-Type: application/json' \
-d '{"username":"alert-test","password":"wrong-password"}'
- Pin field names in CI (continuous integration) with sample logs. If parsing changes, fail the build before deployment.
{"assert":["source.ip exists","event.action in [login_failed,user_created]","service.name exists"]}
- Alert on successful login after repeated failures. This catches the case where brute force turns into compromise.
{"query":"event.action=login_success AFTER event.action=login_failed WITHIN 10m GROUP BY source.ip,user.name"}
- Send alerts to two destinations. Use both chat and email, or chat and pager, so one broken integration does not hide incidents.
Primary: Security Slack/Teams channel
Secondary: On-call email distribution list
- Review suppression rules monthly. Export all muted/suppressed rules and confirm each still has an owner and expiry date.
{"fields":["rule_name","owner","reason","expires_at"]}
View as text
[App/Server Logs] ---- 1. ship ----> [Security Platform]
| |
| 2. raw event search | 3. rule match?
v v
[You confirm] [Detection Rule]
|
| 4. notify
v
[Email/Chat/Pager]This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI