NFC Tag Encoding Best Practices

NDEF Formatting, URI Records, and Batch Encoding

NFC Tag Encoding Best Practices

Encoding a tag correctly the first time saves significant rework cost in production deployments. These practices cover payload design, memory efficiency, write sequencing, locking strategy, and quality verification.

Principle 1: Minimize Payload Size

Every byte saved extends compatibility with smaller chips and reduces write time. The Memory Calculator shows the usable user-memory after subtracting the capability container and TLV overhead.

URI compression: The ndef-uri record's prefix table saves 8–15 bytes on common schemes:

Prefix Code Expands To Bytes Saved
0x01 http://www. 11
0x02 https://www. 12
0x03 http:// 7
0x04 https:// 8
0x05 tel: 4
0x06 mailto: 7

A URL like https://nfcfyi.com/scan/abc123 encodes as [0x04]nfcfyi.com/scan/abc123 — 8 bytes shorter than the raw string.

Use short slugs: For a tag that redirects, the backend URL is irrelevant to the tag payload. Store only the unique identifier and resolve the full URL server-side. https://go.nfcfyi.com/a1b2 fits on every chip including NTAG203 (48 bytes).

Principle 2: Choose the Right Record Type

Use Case Recommended Record Type Avoid
Launch URL URI record (TNF 0x01, type U) Text record with URL string
Display text Text record (TNF 0x01, type T) MIME text/plain
Business contact MIME text/vcard or text/x-vcard AAR alone
Android app launch AAR (External type android.com:pkg) + URI AAR alone (no fallback)
Wi-Fi credential MIME application/vnd.wfa.wsc Plain text SSID/password
Custom app data NFC Forum External type (yourdomain.com:datatype) Raw binary with no type

Always pair an AAR with a URI so iOS users get a useful experience — iOS ignores AAR records.

Principle 3: Write Sequence and Atomicity

The safest write sequence preserves data consistency if the RF link is interrupted:

1. Read and verify tag is blank or re-writeable (check lock bits)
2. Write NDEF payload blocks (data content first)
3. Write TLV 0x03 header and length
4. Write TLV Terminator 0xFE
5. Read back entire NDEF area and verify CRC
6. Only then: set [lock-bits](/glossary/lock-bits/) if required
7. Verify lock bits are set
8. Re-read payload (confirm lock bits do not block reads)

This order ensures a power-fail at any step leaves either a clean blank tag or a fully written tag, never a partial state that reads as corrupt data.

Principle 4: Memory Layout Awareness

Know the exact memory map of your chip before writing. Using the wrong block address overwrites configuration bytes.

Chip Blocks 0-1 Block 2 Block 3 Blocks 4+
NTAG213 UID (7B) + check UID cont. + internal CC (4B) + lock NDEF data (144B usable)
NTAG215 UID (7B) + check UID cont. + internal CC (4B) + lock NDEF data (496B usable)
NTAG216 UID (7B) + check UID cont. + internal CC (4B) + lock NDEF data (872B usable)

Never write to blocks 0-3 through a generic NDEF write API unless you are explicitly reformatting the tag. The capability container at block 3 bytes 12–15 must contain E1 10 {size} 00.

Principle 5: Password Protection Configuration

If using password-protection, configure it after verifying the data payload and before setting lock bits.

NTAG213 password config: - PWD: 4-byte password, default 0xFF FF FF FF - PACK: 2-byte acknowledgement, default 0x00 00 - AUTH0: byte specifying the first protected page (set > max page to disable, ≤ 0x29 to protect pages) - PROT: bit 0 — if 1, password required for reads too; default 0 (writes only)

Common mistake: Writing PACK as 0x00 00 makes the authentication response indistinguishable from no authentication. Use a non-zero PACK so your firmware can confirm it is talking to the correct tag.

Principle 6: Originality Signature Verification

ntag-dna and standard NTAG21x chips carry an originality-signature — a 32-byte ECC signature over the UID signed with NXP's private key. Verify this in your application backend to confirm the tag is a genuine NXP chip, not a clone with a copied UID.

# Pseudocode — verify NTAG originality signature
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend

NXP_PUBLIC_KEY = bytes.fromhex("04...")  # From NXP application note AN11350

def verify_originality(uid_bytes: bytes, signature_bytes: bytes) -> bool:
    key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP128R1(), NXP_PUBLIC_KEY)
    # DER-encode and verify...
    return True  # or False

Principle 7: Bulk Encoding Quality Gates

For production runs of 1000+ tags, enforce automated quality gates:

Gate Check Action on Fail
Pre-encode UID uniqueness in DB Reject duplicate
Post-encode Read-back byte comparison Reject + flag for rework
Post-lock Lock bit verification Reject if not locked
Sampling 1-in-50 full NDEF parse on target phone Pause line if fails

Use the Tag Cost Calculator to budget for a 1-3% reject rate in your unit economics.

See Also

Terms in This Guide