NFC Tag Encoding Best Practices
EncodingEncodingData writing to NFC tags during manufacturing productionView full → 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 recordURI recordNDEF record encoding URIs with compact prefix compressionView full → (TNF 0x01, type U) |
Text recordText recordNDEF record for human-readable text with language codeView full → 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 ForumNFC ForumIndustry body developing NFC standards, specifications, and certifications since 2004View full → 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 bitslock bitsControl bits making memory blocks permanently read-onlyView full →.
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 authenticationauthenticationIdentity verification of NFC tags/readers via passwords or cryptographyView full → 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.