Programmatic SEO and doorway spam are the same production technique — templates filled from data, generating pages at a scale no writer could match. Zillow's page-per-neighborhood and a lead-gen site's page-per-city-times-keyword permutation are built the same way. One is among the most durable strategies in search; the other is named in Google's spam policies.
The difference is usually explained in terms of intent: pages "created for search engines" versus "created for users." Intent is a poor engineering spec — every SEO project intends to rank. The difference worth designing against is mechanical, and you can test for it.
What the policies actually prohibit
Google's spam policies define doorway pages as pages created to rank for specific, similar queries while funneling users to the same destination, or intermediate pages that are less useful than where the user ultimately lands. Separately, the scaled content abuse policy — formalized in Google's March 2024 spam policy update — targets producing many pages primarily to manipulate rankings rather than to help users, and Google was explicit that this applies regardless of how the content is produced. Automation is not the violation; valueless scale is.
Notice what the doorway definition keys on: similar queries, same destination. That is a description of redundancy, not of automation. Which points at the real dividing line.
The mechanical difference is information per page
A useful programmatic page set is a projection of a real dataset: each page renders facts about one entity — a neighborhood's listings, a route's flight times, a compound's properties — that appear on no sibling page. The page set as a whole contains a large amount of information, distributed across pages, and each URL is the canonical home of its slice.
A doorway set inverts this. The underlying information is one page's worth — one service description, one product pitch — and the page count comes from multiplying it against a keyword list. Each additional page adds a heading variant, not information. The set's total information content barely exceeds any single member's.
That is the line, stated mechanically: does page count scale with underlying data, or only with the keyword list? Everything else — quality raters' judgments, ranking systems trained on them, indexing behavior — is downstream of it.
Two tests you can run before building
The substitution test. Take two pages from the planned set and swap their template variables. If the result is the same pages — because nothing but the city name and the header changed — the set is doorways by construction. If substitution produces obviously wrong pages, because the tables, values, and content are entity-specific, the set carries real per-page information.
The lookup test. Ask what question a visitor answers on this page that they could not answer on the sibling page or the category page above it. "Plumbers in Austin" and "plumbers in Dallas" pass if each page carries local listings, prices, or availability. They fail if both pages present the identical pitch with a swapped city name — the classic service-area doorway.
A useful discipline: for each template, count the fields populated from a per-entity data source versus fields that are boilerplate or a spun paragraph around the keyword. There is no documented threshold, but a template that is mostly boilerplate fails the substitution test almost by definition.
How the line shows up in index behavior
You do not have to wait for a manual action to find out which side you are on; index selection tells you earlier. Google decides per URL whether a crawled page is worth indexing, and near-duplicate, low-value pages are exactly what gets left out. A programmatic set drifting toward the doorway side tends to show a poor crawled-to-indexed ratio in Search Console, and deindexing that starts with the thinnest tail of the set.
The reasonable inference — not documented as a formula, but consistent with how Google describes indexing — is that index selection is a continuous quality filter that catches redundancy well before spam enforcement would. Treat your indexing rate per template as the early warning gauge, and treat a decaying rate as the system telling you the marginal pages are not clearing the bar.
Design rules that follow from the mechanism
- Let data determine page count. Generate a page only where the dataset has enough entity-specific content to fill the template meaningfully. Set a minimum-data threshold per page and refuse to publish below it, rather than generating the full combinatorial matrix and hoping.
- No page without a dataset behind it. If a page type cannot name the per-entity data source populating it, it is a keyword multiplied, not an entity served.
- Collapse empty intersections. City-times-category grids always contain cells with no real inventory. Those cells should not exist as indexable URLs; thin intersections are where doorway classification plausibly starts.
- Prune on a schedule. Entities go stale — listings expire, inventory empties. A page whose data has left should be removed or de-indexed, or it rejoins the thin tail.
- Add editorial where stakes are high. Generated data plus genuine per-entity analysis is the strongest configuration; reserve human effort for the highest-value entities rather than spreading spun text across all of them.
What to do
- Run the substitution and lookup tests on every programmatic template you operate, using real page pairs, before Google runs its version.
- Audit template fields: count data-driven versus boilerplate fields, and rebuild templates that are mostly boilerplate around whatever real data you hold.
- Set a minimum-data publishing threshold per page type and gate generation on it.
- Split sitemaps per template and watch the indexed ratio for each — a declining ratio is your earliest signal that the set is sliding toward the thin side.
- Delete or noindex empty intersections and stale entities on a recurring schedule, not as a one-time cleanup.