First hour incident response for a suspected application compromise
For non-engineers who think their web application may have been broken into. This guide gives you a concrete first-hour checklist to contain damage, preserve evidence, rotate access, and verify that the application is no longer actively exposed.
TL;DR — If you think your application was broken into, spend the first hour on containment, not diagnosis: put the app in maintenance mode or block public traffic, preserve logs and snapshots before changing anything, rotate passwords and API keys starting with admin accounts, and check for new users, deployments, and outbound integrations you do not recognize. The most common mistake is cleaning up too early and destroying the evidence you need to understand what happened. Reading time: ~5 min
Goal
By the end of this process, your application is no longer publicly exposed in its normal state, critical access has been rotated, key evidence has been preserved, and you have a short written timeline of what changed and when.
Prerequisites
- Your application hosting login (your cloud, PaaS, VPS panel, or agency portal)
- Your DNS/CDN login if you use one (for example, your provider's dashboard, e.g. Cloudflare: DNS → Records)
- Your identity provider or admin login (SSO — single sign-on — if you use Google Workspace, Microsoft 365, Okta, etc.)
- Access to your email account used for admin alerts
- A place to write notes: a document or ticket with timestamps
- If available: your logging dashboard login (application logs, web server logs, audit logs)
- Optional CLI access for advanced checks:
curlandsshinstalled; check with:
curl --version
ssh -V
Steps
Step 1: Start an incident note with exact times
Create a new document or ticket titled: Suspected compromise - YYYY-MM-DD HH:MM TZ.
Paste this template exactly:
Time noticed:
Who noticed it:
What was seen:
Current public URL status:
Last known normal time:
Changes made during response:
Accounts rotated:
Evidence saved:
People notified:
What you should see when this succeeds: a single running note where every action gets a timestamp.
Step 2: Contain the application from the public internet
⚠️ This can cause downtime. That is the point: stopping ongoing access is usually more important than keeping a possibly compromised app online.
Use the first option you have available in this order:
- In your hosting provider's dashboard: find your app/service and use the stop, pause, disable public access, or maintenance mode control.
- If you use a CDN or WAF (web application firewall), enable "Under Attack", "I'm Under Attack", or maintenance mode in your provider's dashboard.
- If you only control DNS, change the public record to a maintenance page you already trust.
Generic menu paths vary, but look for one of these:
- Hosting dashboard → Application/Service → Settings → Maintenance Mode → Enable
- Hosting dashboard → Networking → Public Access → Disable
- CDN/WAF dashboard → Security → Under Attack Mode → On
- DNS dashboard → DNS/Records → edit
A,AAAA, orCNAMEfor your app hostname
If you have shell access and run nginx, replace the site with a 503 page:
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak.$(date +%F-%H%M)
sudo tee /etc/nginx/conf.d/incident-maintenance.conf > /dev/null <<'EOF'
server {
listen 80 default_server;
listen 443 ssl default_server;
return 503;
}
EOF
sudo nginx -t && sudo systemctl reload nginx
What you should see when this succeeds: visiting the app URL shows a maintenance page, a 503 error, or a provider-generated protection page instead of the normal app.
Step 3: Preserve evidence before cleanup
⚠️ Do not delete files, restart services repeatedly, or redeploy yet. Those actions can erase timestamps and logs.
In your provider's dashboard, create a snapshot or backup of the affected server, database, or app volume.
Typical menu paths:
- Server/VM dashboard → Snapshots/Backups → Create Snapshot
- Database dashboard → Backups → Create On-Demand Backup
- Logging dashboard → Export → Last 24 hours
If you have shell access, collect logs and a file list into one folder:
mkdir -p ~/incident-$(date +%F-%H%M)
cp -a /var/log ~/incident-$(date +%F-%H%M)/logs 2>/dev/null || true
find /var/www -xdev -type f -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sort > ~/incident-files.txt
What you should see when this succeeds: a completed snapshot/backup job and exported logs saved somewhere you will not overwrite.
Step 4: Rotate admin access first
Change passwords for these in this order: hosting account, application admin users, database users, email account used for password resets, and any shared team password vault entries.
Use your provider dashboards:
- Hosting provider → Account/Security → Password → Change
- Application admin → Users/Admins → select each admin → Reset password
- Database provider → Credentials/Users → rotate password
- Email/Identity provider → Security → Change password and sign out of other sessions
If your app stores secrets in environment variables, replace them with new values now:
- Hosting dashboard → App/Service → Settings → Environment Variables → replace
DATABASE_URL,SECRET_KEY,JWT_SECRET,API_KEY,SMTP_PASSWORD, and similar values with newly generated ones
Generate strong secrets locally if needed:
openssl rand -base64 32
What you should see when this succeeds: old passwords no longer work, and the dashboard shows updated secret values or a recent "last changed" time.
Step 5: Revoke sessions, tokens, and API keys
In each system your app uses, revoke active sessions and regenerate tokens.
Look for these menu paths:
- Identity provider → Users → Sessions → Revoke all sessions
- Git provider → Settings → Access Tokens / Deploy Keys → Revoke unknown tokens and rotate known ones
- Cloud provider → API Keys / Access Keys → Disable old key, create new key
- Payment, email, storage, and webhook providers → Developers/API Keys → Roll key
If you use GitHub and have CLI access, list recent workflow and deployment activity from the web UI first; if not possible, at minimum revoke deploy keys in the repository settings.
What you should see when this succeeds: old sessions are signed out, revoked keys show as disabled, and new keys are stored in your app settings.
Step 6: Check for obvious unauthorized changes
In each dashboard, look for anything created or changed recently that you do not recognize.
Check these exact areas:
- Hosting provider → Activity Log / Audit Log → filter to last 24 hours
- Application admin → Users → sort by newest
- Source control → Repository → Commits, Deployments, Secrets, Webhooks
- DNS/CDN → DNS Records, Page Rules/Redirects, Firewall Rules
- Email provider → Forwarding rules, mailbox delegates, app passwords
Record every suspicious item in your incident note with timestamp and screenshot. What you should see when this succeeds: a short list of confirmed-good changes and suspicious changes with times.
Step 7: Notify the right people and freeze further changes
Send one message to your agency, internal owner, or incident contact with the exact text below:
We suspect the application may be compromised. Public access has been restricted, evidence has been preserved, and credentials are being rotated. Do not deploy, restart, or delete anything until we finish review. Current incident note: [paste link].
If you have a status page, post a maintenance notice there rather than in the app. What you should see when this succeeds: one clear thread exists, and nobody is making untracked changes.
Verify it works
Run these checks from a browser or terminal.
- Public app is contained:
curl -I https://your-app.example.com
Expected result: status is HTTP/1.1 503, 403, or a redirect to a maintenance page you recognize.
- Old credentials no longer work:
- Try signing in with one old admin password in a private/incognito window. Expected result: login fails.
- New secrets are active:
- In your hosting dashboard, open App/Service → Settings → Environment Variables. Expected result: updated values show a recent modified time.
- Evidence exists:
- In your provider dashboard, open Snapshots/Backups or Logs/Exports. Expected result: a completed snapshot/backup and at least one exported log file from the affected period.
Common pitfalls
Rotating secrets before saving evidence
Mistake: changing passwords, redeploying, or restarting before taking a snapshot or exporting logs. Symptom: you cannot tell what happened because timestamps and sessions are gone. Fix: create the snapshot/export first, then rotate access.
Leaving one admin path unchanged
Mistake: rotating the app admin password but forgetting the email inbox, SSO account, or hosting account. Symptom: the attacker gets back in through password reset or the control panel. Fix: rotate hosting, email, identity provider, and app admin access in the same session.
Forgetting API keys and webhooks
Mistake: changing human passwords only. Symptom: suspicious outbound traffic or actions continue even after people are signed out. Fix: revoke and regenerate API keys, deploy keys, webhook secrets, and service tokens.
Turning the site back on too early
Mistake: removing maintenance mode after one password reset. Symptom: the app looks normal briefly, then new suspicious changes appear. Fix: keep public access restricted until logs, users, secrets, and recent changes have been reviewed.
Using the compromised app to communicate
Mistake: posting updates inside the same admin panel or email system that may be affected. Symptom: messages disappear, or the attacker sees your response steps. Fix: use a separate trusted channel such as your normal corporate email, phone, or ticket system.
Not writing down exact times
Mistake: relying on memory for when you noticed the issue and what you changed. Symptom: support, legal, or forensic review takes longer because events cannot be lined up. Fix: add a timestamp to every action in the incident note as you go.
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