January 6, 2026•9 min read•RabbitMQ tutorial

RabbitMQ acknowledgements tell the broker what happened after a consumer receives a message. Use ACK when processing succeeds, NACK when processing fails, and choose whether failed messages should be requeued or discarded/dead-lettered.
In this guide, you'll learn how RabbitMQ ACK, NACK, reject, auto-ack, requeue, redelivery and prefetch work, with practical examples.
requeue=true: try delivering the message againrequeue=false: dead-letter or discard the messageWhen RabbitMQ delivers a message to a consumer, it needs to know when it's safe to remove that message from the queue. Without acknowledgments, RabbitMQ has two bad options: remove the message immediately (risking data loss if the consumer crashes) or never remove it (causing infinite redelivery).
Acknowledgments solve this by letting consumers tell RabbitMQ: "I'm done with this message, you can delete it."
A message that has been delivered but not yet acknowledged is called unacknowledged. It stays in the queue, invisible to other consumers, until the consumer acks it, nacks it, or disconnects.
An ACK confirms that the consumer successfully processed the message, so RabbitMQ can remove it from the queue for good.
channel.ack(msg);A NACK tells RabbitMQ that the message could not be processed. The consumer decides what happens next through the requeue argument: put the message back in the queue, or remove it (and dead-letter it when a dead-letter exchange is configured).
channel.nack(msg, false, true); // failed, try again
channel.nack(msg, false, false); // failed, dead-letter or discard| Method | Meaning | What happens to the message |
|---|---|---|
ack | Successfully processed | Removed from the queue |
nack(..., false, true) | Not processed | Requeued |
nack(..., false, false) | Not processed | Dead-lettered or discarded |
reject(..., true) | Reject one message | Requeued |
reject(..., false) | Reject one message | Dead-lettered or discarded |
Use ack when processing succeeds. Use nack when processing cannot continue and you need to choose whether the message should be requeued or removed. Use reject when you only need to negatively acknowledge a single message.
RabbitMQ's automatic acknowledgement mode, often called auto-ack and exposed as noAck: true in some client libraries, considers a message successfully delivered as soon as it is sent to the consumer. The broker doesn't wait for the consumer to finish processing, and the message is removed from the queue immediately.
channel.consume("orders", (msg) => {
processOrder(msg);
}, { noAck: true });This is the simplest mode, but it's risky:
Use noAck: true only when losing messages is acceptable, like for non-critical metrics or logging where occasional data loss doesn't matter.
With manual acknowledgment, the consumer explicitly tells RabbitMQ when a message has been successfully processed. The message stays in the queue (marked as "unacknowledged") until the consumer sends an ack.
channel.consume("orders", (msg) => {
try {
processOrder(msg);
channel.ack(msg);
} catch (err) {
console.error("Processing failed", err);
}
});If the consumer disconnects without acknowledging, RabbitMQ automatically requeues the message and delivers it to another consumer (or the same one when it reconnects).
This is the recommended mode for any production workload.
When you call nack or reject with requeue=true, RabbitMQ puts the unacknowledged message back into the queue so it can be delivered again. The message may be delivered to the same consumer or another consumer.
channel.nack(msg, false, true);Every parameter of channel.nack(message, allUpTo, requeue):
| Parameter | Value | Meaning |
|---|---|---|
message | msg | The message being negatively acknowledged |
allUpTo | bool | true: this message and all previous messages. false: only this message. |
requeue | bool | true: put the message back in the queue. false: drop message. |
Reject the message and requeue it for another delivery. RabbitMQ will attempt to place it back near its original position in the queue, depending on concurrent activity.
channel.consume("orders", (msg) => {
try {
processOrder(msg);
channel.ack(msg);
} catch (err) {
channel.nack(msg, false, true);
}
});The third argument (true) means "requeue this message." Be careful with this pattern: if the message fails every time, it creates an infinite loop of redelivery. Always combine requeue with a retry strategy to avoid this.
Reject the message and remove it from the queue permanently:
channel.nack(msg, false, false);If the queue has a dead-letter exchange configured, the message is routed there instead of being discarded. This is the recommended approach for handling poison messages.
basic.reject and basic.nack can both reject a message and either requeue or remove it. The main practical difference is that nack can reject multiple unacknowledged messages at once using the multiple option, while reject works on a single message.
channel.reject(msg, true); // reject and requeue
channel.reject(msg, false); // reject and discard (or dead-letter)nack was introduced later as an extension to AMQP and is generally preferred since it supports bulk operations.
| ACK | NACK | Reject | |
|---|---|---|---|
| Processing succeeded | ✅ | ❌ | ❌ |
| Requeue message | ❌ | ✅ | ✅ |
| Remove/dead-letter | ❌ | ✅ | ✅ |
| Multiple messages | ✅ | ✅ | ❌ |
| Typical use | Success | Failure/batch handling | Single-message rejection |
The second argument to nack controls whether it applies to multiple messages. When set to true, it negatively acknowledges all unacknowledged messages up to and including the current one:
channel.nack(msg, true, false);This is useful when a batch of messages depends on a shared resource that becomes unavailable.
The same option exists on ack, so you can acknowledge a whole batch with a single frame instead of one call per message:
channel.ack(msg, true);Bulk acknowledgement reduces round trips, but it is all-or-nothing: every unacknowledged message delivered before the current one on that channel gets the same outcome. Use it only when the messages really share a fate.
A RabbitMQ message is marked as redelivered when it has previously been delivered to a consumer and is delivered again, for example after requeueing or when a consumer disconnects before acknowledging it.
When a message is requeued (via nack, reject, or consumer disconnect), RabbitMQ sets the redelivered flag to true. You can use this to detect messages that have been delivered before:
channel.consume("orders", (msg) => {
if (msg.fields.redelivered) {
console.warn("This message was redelivered");
}
processOrder(msg);
channel.ack(msg);
});This flag is useful for logging and debugging, but don't rely on it as a retry counter, it only tells you the message has been delivered more than once, not how many times.
Prefetch count controls how many unacknowledged messages RabbitMQ will deliver to a consumer at once. Without it, RabbitMQ sends messages as fast as it can, which can overwhelm the consumer.
await channel.prefetch(10);
channel.consume("orders", (msg) => {
processOrder(msg);
channel.ack(msg);
});With a prefetch of 10, RabbitMQ delivers up to 10 messages and then waits until some are acknowledged before sending more. This creates natural backpressure and ensures no single consumer hogs all the work.
If you consume with manual acknowledgment but never call ack, nack, or reject, messages stay in the "unacknowledged" state. Once the prefetch limit is reached, RabbitMQ stops delivering new messages to that consumer. The queue appears to stall.
Acknowledging the same message twice throws a channel error and closes the channel. Always ensure your ack/nack logic runs exactly once per message.
Nacking with requeue (channel.nack(msg, false, true)) on a message that always fails creates an infinite redelivery loop. The message bounces between the queue and the consumer forever, burning CPU and blocking other messages. Use a retry pattern with a maximum retry count to prevent this.
When a consumer crashes, its channel closes, or its connection drops, RabbitMQ requeues every message that consumer had not acknowledged yet. That is the behaviour you want, but it has two consequences.
First, a consumer that takes a long time to process a message can be dropped by a TCP or heartbeat timeout while it is still working. RabbitMQ assumes it is dead and requeues all its unacknowledged messages, so the work is done twice. Make sure your connection heartbeat interval is longer than your longest processing time, or break long tasks into smaller chunks.
Second, because a message can be delivered again after any consumer failure, processing must be idempotent. Deduplicate on a message ID or a business key so a redelivery doesn't charge a customer or send an email twice.
nack(msg, false, false) with a dead-letter exchange over blind requeueing for messages that fail deterministically.An ACK confirms that the consumer successfully processed the message, allowing RabbitMQ to remove it.
A NACK negatively acknowledges a message and lets the consumer decide whether it should be requeued or removed/dead-lettered.
It tells RabbitMQ to put the negatively acknowledged message back into the queue for redelivery.
Both negatively acknowledge messages, but NACK supports acknowledging multiple messages at once while reject handles a single message.
The message remains unacknowledged. Once the consumer reaches its prefetch limit, RabbitMQ stops sending it additional messages. If the consumer/channel connection closes, unacknowledged messages can be requeued.
Use manual ACK for workloads where reliable processing matters. Auto-ack is appropriate only when losing a message after delivery is acceptable.
See how RabbitGUI can make your everyday RabbitMQ workflows easier.
Available on Windows, Mac, and Linux.

RabbitMQ tutorialRabbitMQ streams explainedLearn what RabbitMQ Streams are, how they differ from traditional queues, and when to use them for high-throughput event streaming, replay, and fan-out.
RabbitMQ tutorialRabbitMQ ports: 5672, 15672, 25672 and more explainedRabbitMQ's default port is 5672 for AMQP. The Management UI uses 15672, clustering uses 25672 and 4369. Full ports table, Docker mappings and fixes for connection problems.
RabbitMQ tutorialWhat Is RabbitMQ?Learn what RabbitMQ is, how it works, and why it’s used in modern software architectures. Discover RabbitMQ’s benefits, key components, use cases, and how it enables reliable asynchronous communication.
RabbitMQ tutorialSetting up RabbitMQ with Docker and Docker ComposeA complete guide to running RabbitMQ in Docker containers, from quick start examples to advanced configuration options for production environments
RabbitMQ tutorialRabbitMQ Best PracticesA practical guide to RabbitMQ best practices covering queue design, message handling, connection management, monitoring, and production-readiness tips.
RabbitMQ tutorialRabbitMQ vs KafkaEverything you need to know about the differences between RabbitMQ and Kafka.