IMAP and POP3

On this page

IMAP allows clients to operate on the mailbox, message status, and content on the server, making it suitable for maintaining a shared view across multiple devices. POP3 primarily provides listing, downloading, and deleting messages in the maildrop. Both can cache emails locally, and neither implies "delete immediately after download." The real difference lies in how much state and operations the server provides for synchronization.

CapabilityIMAPPOP3
Organize messagesMultiple mailboxes, server-side status, and searchCentered on the current maildrop message list
ReadCan fetch headers, body parts, or attachments onlyRETR fetches the entire message; optional TOP fetches header and partial lines
DeleteSeparate marking and expunging, also supports MOVEDELE marks for deletion; update occurs on normal QUIT
Multi-device consistencyMechanisms like UID, flags, and sync extensionsNo equivalent shared folder/read-sync model
OfflineDepends on downloaded content and local operation queueCan read offline after download
NotificationsIDLE receives mailbox change notificationsBasic protocol usually relies on client polling

This article uses IMAP4rev2 (RFC 9051) as the primary semantic reference. When encountering IMAP4rev1 services, extensions must be determined via CAPABILITY; do not arbitrarily mix old and new response examples. RFC 9051

IMAP Session: Login Does Not Mean Mailbox Selected

The connection goes through states such as unauthenticated, authenticated, mailbox selected, and logout. LIST lists mailboxes after authentication; SELECT selects a read-write mailbox, while EXAMINE provides a read-only view; FETCH, STORE, etc., operate on the selected mailbox. Successful authentication does not automatically imply that all subsequent commands target INBOX.

Commands carry client tags, such as a003; the final OK/NO/BAD with the same tag indicates the command is complete. Untagged responses starting with * report data or state changes and are not the final response to another command; + is a continuation prompt. Client parsers should handle interleaved responses rather than treating "reading one line" as the end of a call.

ExampleHow to Interpret
* 42 EXISTSThere are 42 messages in the current mailbox, not that 42 new messages were added
* OK [UIDVALIDITY 700]The validity value of the current UID namespace
* OK [UIDNEXT 109]The predicted boundary for future UIDs; does not guarantee the next message is exactly 109
a003 OK [READ-WRITE] SELECT completedSelection command completed; mailbox operations can continue

RECENT / \Recent in old IMAP4rev1 is deprecated in rev2; it never directly represented a consistent unread count across all devices. Unread status is typically determined by the presence of \Seen.

Sequence Numbers, UID, UIDVALIDITY: Why Cache Keys Need Three Parts

The message sequence number is the position in the current mailbox, starting from 1. When preceding messages are cleared, subsequent sequence numbers shift forward. The UID is stable and incrementing within the same mailbox and UIDVALIDITY but can have gaps; it is not a global message ID for the account.

Deletion changes current positions but does not change the UID of surviving messages under that UIDVALIDITY Seq 1 UID 101 Seq 2 UID 104 Seq 3 UID 108 Seq 1 UID 104 Seq 2 UID 108 Remove original Seq 1 / UID 101 UIDs belong to the scope of the mailbox and UIDVALIDITY; UIDs cannot be directly reused after moving to another mailbox.

Assume the client sees sequence number 2 corresponding to UID 104, and then another client removes sequence number 1. If you continue to execute STORE 2 based on the remembered position, the current target might already be UID 108. Synchronizers should use UID operations with full scope and promptly update received deletion/sequence change information.

uids = [101, 104, 108]
remembered_sequence = 2
remembered_uid = uids[remembered_sequence - 1]
assert remembered_uid == 104
uids.pop(0)  # Original seq 1 cleared
assert uids[remembered_sequence - 1] == 108
assert uids.index(remembered_uid) + 1 == 1
old_key = ('INBOX', 700, 104)
new_epoch_key = ('INBOX', 701, 104)
assert old_key != new_epoch_key
print('Sequence shift and UIDVALIDITY cache isolation model passed')

Actual cache keys should also include account/server identifiers. When UIDVALIDITY changes, old UID mappings cannot continue to locate server messages; you need to re-enumerate and establish associations, rather than directly applying old offline deletion commands to new UID 104. After copying or moving to another mailbox, the target UID may also change; supported UID mapping responses help update the cache. UIDPLUS RFC 4315

Details of UIDNEXT and Asterisk Ranges

The value of UIDNEXT is used to predict future UID allocations, and UIDs can still skip numbers. Another point often misjudged is that ranges allow endpoints to be in reverse order: if the mailbox's maximum UID is 108, in UID FETCH 109:*, the asterisk represents the largest UID, and it cannot be simply understood as always returning an empty set. Clients should use appropriate search/synchronization methods and verify that the returned UIDs are indeed higher than the local watermark.

Non-existent specified UIDs are usually ignored, and command OK does not guarantee the target message was found. Therefore, operation completion, target existence, and target status matching expectations are three different observations.

After deleting the first message, is sequence number 2 still the same message?

After clearing the first message, sequence number 2 points to UID 108, while UID 104 moves to sequence number 1. Rebuilding the mailbox, even if 104 reappears, old cache keys cannot be reused; what changes is the identity scope, not just list reordering.

Preparing the visual
After deleting the first message, is sequence number 2 still the same message?

Sequence numbers move with the current position; UIDs depend on the mailbox and UIDVALIDITY scope. Same numbers do not mean the same object identity.

Fetching Content, Modifying Flags, and Reading Literals by Byte

UID FETCH 104 (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)]) can first fetch fields needed for the display list, avoiding downloading the entire large attachment. BODY.PEEK[...] does not implicitly set \Seen due to reading content; the corresponding fetch behavior of normal BODY[...] may set it.

When returning content, the server can use {N} literals, where N indicates the number of subsequent raw bytes. The body may contain line breaks, parentheses, or text that looks like responses; the parser must read exactly N bytes before resuming protocol parsing, and cannot simply look for the closing parenthesis by line.

payload = 'Subject: Status\r\n'.encode('utf-8')
assert len(payload) == 17  # Five Chinese characters 15 B, plus CRLF 2 B
wire = b'{17}\r\n' + payload + b')\r\na004 OK done\r\n'
header, rest = wire.split(b'\r\n', 1)
size = int(header[1:-1])
literal, tail = rest[:size], rest[size:]
assert literal.decode('utf-8') == 'Subject: Status\r\n'
assert tail.startswith(b')\r\na004 OK')
assert len(literal) != len(literal.decode('utf-8'))
print('UTF-8 literal byte-delimited parsing passed')

Marking as read can be done with UID STORE 104 +FLAGS.SILENT (\Seen). +FLAGS adds specified flags; using FLAGS replaces the entire set and may overwrite updates from other clients. .SILENT suppresses the corresponding flags data echo generated by this operation, but does not close all server state notifications.

Deletion and Move: Operations Do Not Share the Same "Delete Button" Semantics

Setting \Deleted is just a mark; EXPUNGE actually clears messages marked for deletion in the selected mailbox. A normal EXPUNGE may clear messages marked by another client as well, so you cannot assume it only processes UIDs you just operated on. When supported, UID EXPUNGE with a limited UID set can narrow the scope.

MOVE moves messages from one mailbox to another. Old clients simulate this via COPY, marking for deletion, and EXPUNGE, which involves intermediate states and failure windows; dedicated MOVE avoids issues with partial composite operations, but multi-message operations still need to handle partial failures and cannot describe a bulk move as an all-or-nothing transaction like an application database. MOVE RFC 6851

CLOSE may clear messages marked for deletion in a read-write selected state; UNSELECT exits the selected state without performing this clearing. When doing read-only checks, prioritize understanding EXAMINE semantics and do not arbitrarily call operations with deletion consequences just to "exit the mailbox."

IDLE is Change Notification, Not a Persistent Event Log

The client sends a tagged IDLE, and the server returns + and keeps the command waiting, during which it can report changes like EXISTS or flags. When the client wants to execute the next normal command, it first sends an untagged DONE, waits for the IDLE tag completion response, and then continues.

MomentExample InteractionClient Next Step
Start Waitinga010 IDLE, server + idlingContinuously read untagged responses
New Status Hint* 43 EXISTSRecord what needs syncing, do not treat the number as UID
End WaitingDONE, server a010 OK ...Then initiate FETCH/SEARCH, etc.
Connection DropNo persistent event position guaranteeReconnect, check UIDVALIDITY, catch up on state

The specification recommends ending and resending IDLE within an interval of no more than 29 minutes to accommodate server idle timeouts; intermediate NAT or mobile device sleep may still cause earlier disconnections. IDLE provides timely notifications but does not promise fixed latency, always-online status, or replayable message notifications during disconnections. IMAP IDLE

Incremental Synchronization and Concurrent Conflicts

Comparing only UIDNEXT can detect some new changes, but cannot know about changes to old message flags or cleared UIDs. Services supporting CONDSTORE use MODSEQ to identify state changes; QRESYNC further supports fast resynchronization and VANISHED deletion information. Before use, you need to enable them according to protocol version and capabilities; do not assume all services implement them. CONDSTORE/QRESYNC RFC 7162

Assume the client reads message MODSEQ 20, and during offline time, another client changes it to 21. A conditional STORE with UNCHANGEDSINCE 20 can reject overwriting the new state and inform the unapplied target via MODIFIED. The client should re-read and decide on merging, rather than ignoring the conflict and continuing to advance the local watermark.

The recovery process is therefore: verify mailbox and UIDVALIDITY, supplement additions, changes, and deletions, confirm local data is persisted, and then save the sync watermark. IDLE hints and sync watermarks are not the same thing; updating the watermark before saving actual content may lead to mistakenly believing synchronization is complete after a crash.

POP3: Download, Mark, and Update Are Three Separate Actions

After POP3 authentication, it enters TRANSACTION; LIST/STAT view messages and sizes, RETR downloads, and DELE marks for deletion. Only a normal QUIT from the transaction state enters UPDATE, executing deletions based on marks; RSET can clear deletion marks, and abnormal disconnections should not enter deletion updates solely due to previous DELE. POP3 RFC 1939

POP3: RETR downloads, DELE marks, normal QUIT enters update TRANSACTION: Authenticated DELE 1 after RETR 1 Download and mark are two independent commands Receive QUIT → UPDATE Execute deletion based on marks and return results Drop or RSET Abnormal drop does not delete based on marks; RSET clears marks When QUIT response is lost, results are still ambiguous; local save and server deletion are not the same atomic transaction.

Therefore, "POP3 download equals delete" is not a protocol rule. Clients can retain server copies, or request deletion after successfully saving local copies. If multiple devices retain copies, they can each download, but there is no shared read and folder synchronization semantics like IMAP.

POP3 message numbers are only used for positioning within the current maildrop session and may change across sessions. Optional UIDL provides cross-session identifiers, allowing clients to avoid duplicate downloads; it is not an RFC 5322 Message-ID. The specification even allows special cases where identical copies have the same unique-id; do not treat UIDL as a globally unique UUID that can ignore duplicates.

Local save and server UPDATE also do not share a common transaction: if you record "downloaded" first and then persist content, a client crash may result in missed storage; if content is saved but local records are not yet committed, reconnecting may cause duplicate downloads. When QUIT is executed but the response is lost, the deletion result is also ambiguous. Archiving programs need to reliably commit local content and indexes, and re-enumerate server state upon reconnecting.

After DELE and a drop, is the message deleted?

RETR then DELE; the server copy remains; after reconnecting from an abnormal drop, the mark disappears and the message is retained. Only after resetting and proceeding with a normal QUIT does the server delete. If you delete and QUIT without downloading, this model also exposes the result of having no local copy.

Preparing the visual
After DELE and a drop, is the message deleted?

RETR copies content, DELE only marks, normal QUIT enters UPDATE. Abnormal drops do not automatically treat uncommitted marks as deleted.

TLS, Authentication, and Selection Criteria

IMAP commonly uses TCP 993, POP3 commonly uses TCP 995 for implicit TLS; 143/110 can be upgraded via STARTTLS/STLS respectively, with specific capabilities needing negotiation. Server identity verification should be completed before sending passwords; whether OAuth or other SASL mechanisms are supported is determined by server capabilities; do not skip certificate verification just because the connection uses TLS. Email Access TLS RFC 8314

Multi-device collaboration, server search, and folder synchronization fit the IMAP state model better; one-way download to an independent archive library can use POP3, but "simple" does not automatically eliminate backup, deduplication, and deletion timing issues. IMAP synchronization is also not a backup: accidental deletion on the server can be synchronized to all clients; independent version retention and recovery points are another capability.