Presidio is no longer a Microsoft project. The repository moved to an independent, community-governed organisation in June 2026, the migration notice landed on 24 June and the container images moved on 28 June, and the legacy Microsoft registry path still resolves while serving frozen builds. The architecture split matters more than any leaderboard: Presidio ships 81 entity types of which only three (person, location, and nationality or religious or political group) come from the NER model, so it is a pattern engine with checksums and a name detector bolted on, while GLiNER is a zero-shot span model whose entity schema is a runtime argument. They fail differently: Presidio's recall collapses on partially disclosed and obfuscated identifiers, and GLiNER has no checksum, no format validation, and a label prompt that behaves like an untuned hyperparameter. There is no credible published head-to-head between them, so do not trust any blended F1 you are shown. Use Presidio as the spine for structured identifiers, add GLiNER only for name-shaped classes and free-text identifiers you cannot express as a regex, pin both versions exactly, and build a per-class recall eval on your own text because nobody else's numbers describe your data.
The old Microsoft repository path for Presidio now answers with an HTTP 301. In June 2026 the project moved out of the Microsoft organisation into an independent, community-governed home, and every page ranking for "presidio vs gliner" still calls it Microsoft Presidio. If your data protection impact assessment cleared this component partly because a Microsoft name appeared in the path, that assessment describes a project that no longer exists in the form you approved.
The second thing is less comfortable: there is no credible published head-to-head between these two. The blogs quoting precision and recall do not state which Presidio version, which recognizer configuration, or which GLiNER checkpoint produced the numbers, which makes them unfalsifiable rather than wrong. So this post does not rank them. It compares them by architecture and failure mode: which entity classes each structurally misses, and where the redaction hop belongs when the model is already inside your perimeter. Everything below was checked against Presidio 2.2.364 and GLiNER 0.2.28 on 13 August 2026.
Presidio is no longer a Microsoft project
The repository now lives at the data-privacy-stack organisation, showing 10,461 stars, 1,242 forks and 54 open issues, with the last push on 11 August 2026. It is live, not abandoned.
The dates matter for anyone reconciling an old review. The migration notice was committed on 24 June 2026 and the container images moved four days later. Version 2.2.363, released 28 June, was the first cut under the new organisation; 2.2.364, released 22 July 2026, is current and needs Python 3.10 or later.
The project's transition document is explicit about governance: Presidio is community-governed, stays under the MIT licence, will not be owned or operated by a commercial entity, and the technical steering committee is expanding to include maintainers from outside Microsoft. Microsoft supports the move but no longer owns it.
The dangerous part is the registry. Images moved to the GitHub Container Registry, and the legacy Microsoft path still resolves without being updated, so its latest tag no longer points at the newest release.
# resolves, never updated again, :latest is stale mcr.microsoft.com/presidio-analyzer:latest # current, pin the tag ghcr.io/data-privacy-stack/presidio-analyzer:2.2.364
If your manifests or your air-gapped mirror still point at the Microsoft registry you are shipping a frozen redaction layer, and your scanner will not flag it, because a stale image with no new CVEs looks exactly like a healthy one. Same silent staleness as in how to prevent data leakage in AI applications: present in the diagram, inert in production.
Two different machines: rules with checksums versus a zero-shot span model
Count Presidio's entity table at 2.2.364 and you get 81 supported types: 13 global, 60 country-specific across 18 countries, and 8 medical. Now read the detection-method column. Only three global types (person, location, and nationality or religious or political group) come from the NER model. Everything else is a regular expression with a context window and, where the format allows, a checksum. That ratio is the architecture in one line: a pattern engine with a name detector bolted on, not the reverse. Presidio's precision on a card number is not a model property, it is arithmetic.
GLiNER is a different machine. The published architecture takes the text and the entity types you want as one joint input, projects both into a shared latent space with a bidirectional encoder, and scores candidate spans against label embeddings in a single forward pass (arXiv:2311.08526). Your entity schema is therefore a runtime argument. You can ask for "patient chart number" at 09:00 and "broker account reference" at 09:05, no retraining, no code change.
That joint encoding is also why a span model stays cheap as the schema grows: published measurements on the successor architecture put per-label classifiers at 6.8x slower at 20 labels, because they run one pass per label (arXiv:2507.18546). Going from 5 entity types to 25 costs sequence length, not five times the passes. For a per-jurisdiction policy where the entity list differs by tenant, that is the whole reason to consider a span model.
Entity coverage: what ships, what you define, what nothing catches
The last row is the one that ends careers: neither architecture catches the combination that re-identifies a person when each component is innocuous alone. If your compliance narrative treats a redaction pass as sufficient, the tool's own documentation contradicts you on its first page, stating that because detection is automated there is no guarantee it finds all sensitive information. Redaction is a mitigation, not a control. Put the control at the access layer, as we argue in cloud vs on-premise AI security and cost.
| PII class | Presidio | GLiNER | Use |
|---|---|---|---|
| Payment and bank identifiers | Regex plus checksum | Span guess, no arithmetic | Presidio, always |
| National IDs, 18 covered countries | Ships with context words | Label per ID, no format check | Presidio, confirm coverage |
| National IDs elsewhere | Custom recognizer you maintain | One label string, at runtime | GLiNER, unless there is a checksum |
| Person names | spaCy NER, English default | Core strength, zero-shot | GLiNER, spaCy recognizer removed |
| Addresses and locations | Coarse LOCATION span | Label prompt, weakest class | Both, lowest recall here |
| Free-text role identifiers | Custom recognizer per entity | Runtime label, no retraining | GLiNER, this is its job |
| Medical and clinical | 8 shipped types | Label prompt for the rest | Presidio, GLiNER for the tail |
| Partial or obfuscated values | Recall collapses | Degrades, does not collapse | GLiNER, lower threshold |
| Quasi-identifiers (role, employer, city) | Not modelled | Not modelled | Neither. Policy, not detector |
Where each one systematically fails
# pin all three; treat a minor bump as breaking presidio-analyzer==2.2.364 presidio-anonymizer==2.2.364 gliner==0.2.28
Presidio's structured-identifier ceiling
Presidio is excellent at values that arrive whole and well formed, and it degrades sharply when they do not. The REDACT benchmark, a controlled multilingual PII evaluation covering 25 languages and 9 scripts, is the closest thing to a public evaluation scoring both approaches (arXiv:2606.19881). Absolute scores were low across the open detectors, and the paper does not state which Presidio version, which configuration, or which GLiNER checkpoint it ran, so the numbers characterise naive defaults under deliberately adversarial conditions, not either tool's ceiling. Treat it as evidence about failure modes, not a head-to-head for a procurement deck. Three findings are useful. Recall on partially disclosed identifiers fell to 0.02 for the rule-based detector against 0.23 on complete ones, exactly what format validation predicts: half an account number is not a valid account number, so the pattern never fires. Name-component types were weak across every detector tested. And triple-name decomposition (patronymics and naming systems where position does not determine role) is the one release gate the benchmark's own corpus does not fully clear. The second failure mode is quieter: the context window. Confidence is boosted by nearby trigger words, so a bare identifier in a table cell scores lower than the same one in a sentence. Teams tune a threshold on prose, then run it over exported spreadsheets.
GLiNER's label prompt is an untuned hyperparameter
GLiNER's flexibility is its failure mode. The label string is part of the input, not a name for something you already trained, and "SSN", "social security number" and "US national identifier" are three queries returning three different span sets. Nobody versions these strings, so the policy that passed review in March is not the one running in August. Boundary errors are next. A span model returns offsets it inferred, so it can hand back "Smith" where you needed the full name, and your anonymiser replaces exactly what it was given. Rules engines get boundaries right by construction. Then the supply chain. GLiNER is an optional install extra in Presidio, not a default dependency, and the declared range accepts anything from 0.2.26 up to 1.0. The library sits at 0.2.28, Apache-2.0 and pre-1.0, so there is no API stability contract. The PII-tuned checkpoint its sample recommends is fine-tuned from the multilingual base on a synthetic dataset, so it learned the shapes of invented identifiers rather than the ones in your ticket queue, and its model card had not been touched since April 2024. The base model carries roughly 289 million parameters, setting your memory and latency floor.
Names and addresses outside English
Out of the box Presidio is an English system running the large spaCy English pipeline: the documentation states that the default configuration contains recognizers and models for English. Supporting a second language is two jobs, not one. The first is a config file:
nlp_engine_name: spacy
models:
- lang_code: en
model_name: en_core_web_lg
- lang_code: de
model_name: de_core_news_lgThe second job is rewriting the context words, because the confidence boost that fires on the token "passport" does nothing next to "Reisepass" or "паспорт". Teams routinely do the first job, skip the second, and ship a detector that finds candidate spans and scores every one below threshold. The failure is invisible: your logs show a clean pass rate indistinguishable from text with no PII in it.
GLiNER does not rescue you by default either. The PII checkpoint its sample points at is tagged for exactly six languages, all Latin script: English, French, German, Spanish, Portuguese and Italian. Neither project publishes per-script recall for non-Latin writing systems, and we will not invent a number. What configuration alone tells you is that a Cyrillic, Arabic, Thai or CJK corpus sits outside the tagged scope of the checkpoint and outside the default scope of the rules engine, so both need validation on your own text before anyone signs a data flow diagram. If your obligations span the EU, the consequences are in GDPR and AI: handling EU customer data.
The recall versus latency trade
Two changelog entries mark the sharp edges. Version 2.2.361 added automatic device detection with a documented 4x to 10x speedup for the GLiNER and transformer paths, a polite way of saying earlier releases quietly ran your span model on CPU while a GPU sat idle. Version 2.2.362 added an ONNX Runtime flag because GLiNER crashed with an illegal instruction on CPUs without AVX2. On older Xeon hardware that flag is not an optimisation, it is the difference between running and a core dump.
For budgeting, published figures for a GLiNER-family safety and PII encoder give a usable order of magnitude, scoped to that paper's hardware: 19 and 20 milliseconds per request at batch size one on a datacentre GPU for its 145M and 147M compact encoders, and about 194 requests per second under dynamic batching at a 480 ms median and 900 ms P99 (arXiv:2605.05277). Batch size one is fast; throughput mode buys volume at the cost of the tail, and if your hop is synchronous in front of a streaming response the P99 is what users feel. The same paper puts name detection in the mid-70s F1 and address in the mid-to-high 60s on a Russian PII benchmark: address is the harder class, in every language.
The integration is real but not a drop-in. You map GLiNER's labels onto Presidio's entity vocabulary by hand, and the sample says to remove the spaCy recognizer first, otherwise both NER paths fire and every name returns as two overlapping spans. Note the default device:
recognizer = GLiNERRecognizer(
model_name=GLINER_PII_CHECKPOINT, # multilingual PII checkpoint
entity_mapping=ENTITY_MAPPING, # GLiNER label -> Presidio entity
flat_ner=False,
multi_label=True,
map_location="cuda", # the sample ships "cpu"
)
registry.remove_recognizer("SpacyRecognizer")Nobody warns you if map_location="cpu" reaches production. You just run a 289M encoder on a CPU core.
| Workload | Redaction sits | Dominant cost | Config that matters |
|---|---|---|---|
| Interactive chat, streaming | Gateway hop | Batch-1 pass, tens of ms | Batch 1, pinned to GPU, no batching |
| High-volume async API | In-process | Queue wait, sub-second P99 | Cap batch window, alert on P99 |
| Bulk ingestion, eval prep | Offline job | Throughput only | Both detectors in series |
| CPU node without AVX2 | In-process | Process crash | load_onnx_model=True, 2.2.362+ |
| Long documents | Chunked | Linear in chunks | Budget per chunk, not per request |
Where the redaction layer belongs on your own hardware
A redaction service that calls a hosted API to decide what is sensitive has already disclosed the thing it was protecting. That rules out managed detection endpoints for regulated work, and it is why both tools here earn their integration cost: they run in your process or your cluster, no egress.
Once the weights are inside the perimeter the purpose of the layer changes, and most teams do not update their thinking to match. Redaction stops being about data leaving the building and becomes internal least-privilege: one tenant's identifiers out of another tenant's retrieved context, raw values out of prompt logs and traces, identifiers out of the eval corpus engineers open on laptops.
For a private deployment that usually means the hot path needs a thinner layer than a cloud pipeline does, or none at all, while everything persisted needs a strict one. Redacting a prompt on its way to a model already cleared for that data buys nothing and costs you a P99. Redacting the same prompt on its way to the trace store buys what you needed.
Three placements, in order of how often they are right:
Be precise about scope: this is data minimisation, not content safety. Jailbreak filtering, output validation and topical policy are a different stack, compared in AI guardrails compared: NeMo Guardrails, Guardrails AI and Llama Guard. Conflate them and you get a guardrail framework catching PII badly and a PII detector missing prompt injection entirely.
The decision path, and the eval you have to build
Use Presidio as the spine. It is the only one of the two with checksum validation, an explicit technical steering committee, and a release cadence you can point a vendor questionnaire at. The GLiNER reference implementation is Apache-2.0, public since November 2023, and sits at 3,531 stars with 92 open issues and a commit three days old. It is maintained, and it is a research codebase with a research codebase's governance surface. Those answers are not equivalent when someone asks who is accountable for a security patch.
Add GLiNER second, in series, for three things: person names, addresses, and free-text identifiers you cannot express as a regex. Run Presidio alone if your PII is structured and your text is English. Run GLiNER alone almost never: you would be discarding free arithmetic validation.
Whatever you pick, the eval is not optional and nobody can hand it to you:
For this under a specific regime, see HIPAA-compliant AI in healthcare, and for the controls around it, our AI security pillar.
In a security review, the component that fails is rarely the detector. It is the missing evidence: no per-class recall numbers, no record of which version produced them, and an image pulled from a registry that stopped updating in June.
FAQ
Quick answers to the questions this post tends to raise.



