Separate audit history from diagnostic logging
Application logs explain how software behaved: errors, latency, cache decisions, and dependency calls. Audit records answer a different question: who performed a meaningful action, on which resource, under what authority, when, and with what outcome.
Application logs
Optimized for debugging and operations, with short-lived technical context and sampling.
Security events
Optimized for detection: failed sign-ins, unusual access, policy violations, and alerts.
Audit records
Optimized for accountability, investigation, customer history, and compliance evidence.
One event may produce all three, but they can have different schemas, access controls, retention periods, and storage systems.
Design a stable event schema around facts
Use a versioned event name such as project.member.invited and record the trusted actor, tenant, target resource, action, outcome, timestamp, request ID, and relevant authorization context. Preserve IDs even when display names later change.
{
event: "invoice.approved",
version: 1,
actor: { type: "user", id: actor.id },
tenantId: actor.tenantId,
target: { type: "invoice", id: invoice.id },
outcome: "success",
requestId,
occurredAt: new Date().toISOString()
}Store small, intentional metadata needed to understand the decision. Avoid copying entire request bodies, database rows, tokens, passwords, payment details, or sensitive documents into the event.
Write audit records beside the protected mutation
Create the record on the server after identity and authorization are established. Do not trust a client-supplied actor, role, tenant, timestamp, or success outcome. For database changes, write the business record and audit event in the same transaction when possible.
Failures may also matter. Record denied access and rejected high-risk actions through a separate, carefully rate-limited security path so an attacker cannot create unlimited audit volume.
Make audit history difficult to alter silently
Use append-only permissions for the application identity and reserve correction workflows for a tightly controlled administrative service. Encrypt records in transit and at rest, back them up independently, and limit who can search or export them.
- Prevent ordinary application code from updating or deleting historical events.
- Record exports and administrative access to the audit system itself.
- Use immutable storage, signed batches, or hash chaining where the risk model justifies it.
- Test restoration and verify record counts and integrity after recovery.
Tamper evidence does not replace access control; it helps reveal unauthorized changes after preventive controls fail.
Collect the minimum evidence and retain it deliberately
Audit data can become sensitive because it links people, actions, organizations, devices, and time. Define a lawful purpose, access policy, retention period, deletion or anonymization process, and export controls with the appropriate privacy and compliance stakeholders.
Prefer stable internal identifiers over email addresses or names. When investigators need display data, resolve it under current permissions rather than duplicating personal information into every event. Never place raw session tokens, authorization headers, secrets, or unredacted form data in metadata.
Build investigation workflows, not just a table
Support bounded searches by tenant, actor, target, event type, outcome, and time window. Use cursor pagination, enforce tenant scope on every query, rate-limit expensive exports, and generate large exports asynchronously with short-lived authorized download links.
Human-readable activity feeds can be derived from canonical events, but presentation copy should not become the stored source of truth. Version event interpretation so old records remain understandable after the UI changes.
Monitor the audit pipeline itself
Measure accepted events, rejected schemas, write latency, queue lag, storage errors, export volume, and gaps between successful sensitive mutations and audit persistence. Alert when the pipeline stops receiving expected events or falls behind.
Correlate audit records with the request and deployment evidence described in our Next.js observability guide. Review access boundaries alongside our authentication and RBAC guide.
Next.js audit logging checklist
✓ Audit records are separate from debug logs
✓ Event names and schemas are versioned
✓ Actor and tenant come from trusted identity
✓ Sensitive mutations persist required evidence
✓ Application identities cannot rewrite history
✓ Metadata excludes secrets and excess PII
✓ Searches and exports enforce tenant scope
✓ Audit-pipeline gaps trigger alerts
Build a more dependable Next.js application
Endurance Softwares helps teams design, build, test, and operate production-ready Next.js platforms.
