WordPress

Your Site Has a Page for Every Month You Ever Published: A WordPress Date Archives Guide

12 min read

Somewhere on your WordPress site there’s a page listing every post you published in March 2019. Nobody linked to it. Nobody designed it. You’ve almost certainly never opened it. But it exists, it’s crawlable, and Google may well have it in the index next to the posts it duplicates.

The last content audit we ran for a client turned up roughly 1,400 indexed URLs against 180 actual posts. The gap wasn’t spam or a hacked directory — it was years of automatically generated year, month, and day listings, each one a thin re-run of content that already had a proper home. The site owner had no idea those pages were there, because WordPress never asks permission to make them.

Date archives are one of the few WordPress features that ship on by default, cost you something, and have no settings screen. This guide covers what core actually builds, why the URLs look the way they do, when the archives are worth keeping, and how to shut them up when they aren’t.


What WordPress Builds Every Time You Publish

Publish a post and WordPress quietly makes it reachable from more places than the permalink you chose. The post itself, sure. But also its category archive, its tag archives, its author archive, and — the one people forget — up to three separate date archives: a year listing, a month listing, and a day listing.

Three date URLs per post, whether you asked or not

A post published on 14 March 2019 is listed on /2019/, /2019/03/, and /2019/03/14/. Each of those is a real, crawlable, paginated archive page rendered by your theme. Core routes them with the query conditionals is_year(), is_month(), and is_day(), with is_date() as the catch-all, which is true for any date-based archive query — the three above, plus time-based ones like hour and week archives (all four live in wp-includes/query.php). The matching URL builders are get_year_link(), get_month_link(), and get_day_link() in wp-includes/link-template.php.

Because those archives inherit the main query, they also inherit the Blog pages show at most setting from Settings → Reading. The posts_per_page option defaults to 10, so a year in which you published 47 posts becomes five paginated archive pages, not one. Multiply that across a decade of publishing and the arithmetic gets uncomfortable fast.

You have date archives even with /%postname%/ permalinks

This is the part that surprises people. Plenty of site owners assume that because their permalinks are set to Post name — no year, no month, nothing date-shaped in the URL — they don’t have date archives at all. They do.

Core builds the date permalink structure separately from the post permalink structure. In WP_Rewrite::get_date_permastruct(), WordPress looks through your permalink structure for one of three accepted date orderings, and if it finds none, the code falls back to a default: %year%/%monthnum%/%day%. The only condition that turns date archives off entirely is an empty permalink_structure — in other words, Plain permalinks, which nobody should be running anyway.

So the practical rule is that if you’re on pretty permalinks of any flavour, you have date archives. Try it: append /2019/03/ to your domain and see what comes back.

WHY IT MATTERS

Date archives are pages you didn’t write, competing in an index you don’t control, for queries you never targeted. On a news site that’s a feature. On a small business site with 60 blog posts, it’s dozens of near-empty listings diluting the handful of pages you actually care about — and the fix takes about ten minutes once you know where to look.

How the URLs Actually Get Built

Understanding the rewrite side saves you a lot of guessing when a date URL doesn’t look the way you expected.

Core accepts exactly three date orderings

Reading get_date_permastruct() in wp-includes/class-wp-rewrite.php, the accepted structures are %year%/%monthnum%/%day%, %day%/%monthnum%/%year%, and %monthnum%/%day%/%year%. The comment above them is blunt about the constraint: the date permalink must have year, month, and day separated by slashes. WordPress scans your permalink structure for whichever of the three appears, and uses it. Miss all three and you get the first one by default.

That’s why a custom permalink structure with dates in an unusual arrangement can still produce conventional /YYYY/MM/DD/ archive URLs. Core isn’t reading your intent, it’s pattern-matching against a fixed list.

When %post_id% pushes archives under /date/

There’s a collision-avoidance rule in the same function worth knowing about. If %post_id% shows up among the first three tokens of your permalink structure, core prefixes the date structure with date/ so numeric post IDs and numeric years can’t fight over the same URL space. Your date archives then live at /date/2019/03/ instead of /2019/03/.

Nothing’s broken when this happens — it’s a deliberate guard. But if you go hunting for /2019/ on a site using the Numeric permalink preset and get a 404, this is why.

The template doing the rendering

Date archives fall through the standard template hierarchy. Core’s get_date_template() asks for a date.php template first; if your theme doesn’t ship one, it falls back to archive.php, then index.php. Most themes never bother with date.php, which means your date archives are rendered by the same generic archive template as your categories and tags — with a heading supplied by get_the_archive_title().

That function is worth a look, because it’s where those slightly awkward page headings come from. Core prefixes date archive titles with Year:, Month:, or Day: depending on which conditional matches, so visitors land on a page titled something like “Month: March 2019”. It’s honest, it’s translatable, and it reads like a database dump. Both the prefix and the finished title pass through the get_the_archive_title_prefix and get_the_archive_title filters, so a theme, plugin, or locale can change what actually renders. If you’re keeping your date archives, that heading is the first thing to rewrite.

QUICK REFERENCE

The core pieces you’ll reach for when working on date archives:

Conditionalsis_date(), is_year(), is_month(), is_day()
URL buildersget_year_link(), get_month_link(), get_day_link()
RewriteWP_Rewrite::get_date_permastruct()
Templatedate.phparchive.phpindex.php
Headingget_the_archive_title()
Archive listwp_get_archives()
Robots — the wp_robots filter, wp_robots_no_robots()

The SEO Problem Nobody Configured

Date archives aren’t inherently harmful. What makes them a problem is that they’re generated at volume, filled with content that already lives elsewhere, and left entirely unmanaged.

Thin listings competing with the real thing

A monthly archive is a list of post titles and excerpts pulled from posts that each have their own indexed URL. There’s no original writing on it, no unique heading beyond “Month: March 2019”, and often no useful internal purpose. When Google has to choose which of your URLs best answers a query, you’ve handed it several near-identical candidates instead of one clear one.

Worth being precise here, because the internet loves this myth: there’s generally no “duplicate content penalty” for this kind of thing. Google’s own guidance on consolidating duplicate URLs describes picking a canonical version and consolidating signals, not punishing you. The real costs are split signals and wasted crawling, which is bad enough without inventing a penalty on top. If you want the full picture on how Google handles near-identical URLs, our canonical URL guide covers the mechanics.

Core’s sitemap never mentions them

Core’s own sitemap declines to mention them. WordPress has generated an XML sitemap automatically since 5.5, and it registers exactly three default providers — posts, taxonomies, and users. You can see them side by side in wp-includes/sitemaps/class-wp-sitemaps.php. Categories and tags get listed. Author archives get listed. Date archives get nothing, because there’s no date provider to give it to them. The sitemap system is extensible — a plugin can register its own provider via wp_register_sitemap_provider() — but nothing in core does.

So core builds these pages, links to them from the archive widget if your theme uses one, and then declines to tell search engines they exist. Search engines find them anyway, through your own internal links and through crawling — a sitemap has never been the only way in. But the omission is a reasonable signal that nobody considers date archives high-value.

Crawl budget and the long tail

On a small site, crawl budget genuinely isn’t your bottleneck and you shouldn’t lose sleep over it. On a site with a decade of archives, a few thousand posts, and pagination compounding across every year and month, the math changes: crawlers spend time re-fetching listing pages that rarely rank, while your genuinely new content waits its turn.

The honest framing is that date archives are a low-grade tax. Small sites pay a rounding error. Large publishing archives pay real money.

Keep Them, noindex Them, or Turn Them Off

Three viable answers, and which one’s right depends on what your site actually is.

When date archives earn their keep

News sites, magazines, and anything where recency is the organising principle have a legitimate use for these pages. If readers genuinely browse by “what happened in October”, a date archive is the right tool and you should style it properly, write a real heading, and let it be indexed.

They can also be useful internally even when they’re closed to search engines. An editor pulling up everything published last quarter is a perfectly good reason to keep the URLs working — noindex doesn’t take the page away from humans, it just asks search engines not to list it.

noindex is the usual answer

For most business sites, blogs, and portfolios, the sensible default is to leave the pages reachable and ask search engines not to index them. Every major SEO plugin exposes this as a toggle — Yoast, Rank Math, SEOPress and AIOSEO all have an archives section in their settings where date archives can be set to noindex, though the exact labels move around between versions, so go by what the screen says rather than what a tutorial from 2021 said.

Without a plugin, core’s wp_robots filter does the job in a few lines. Both the filter and wp_robots_no_robots() arrived in WordPress 5.7, and the helper emits noindex, follow rather than a bare noindex. Put this in a small site-specific plugin or your child theme’s functions.php:

add_filter( 'wp_robots', function( $robots ) {
    if ( is_date() ) {
        return wp_robots_no_robots( $robots );
    }
    return $robots;
} );

One caveat that catches people out: the page has to stay crawlable for this to work. Google’s documentation on blocking indexing is explicit that a URL must not be blocked in robots.txt and has to be otherwise accessible, or the crawler never sees the noindex directive you added, and the page can still show up in results — for example, if other pages link to it. Blocking date archives in robots.txt and noindexing them is the combination that fails — the robots.txt rule prevents the noindex from ever being read. Pick one, and for de-indexing, noindex is the one that works.

And keep expectations sensible about timing. Removing pages from the index takes as long as it takes crawlers to revisit them, which can run from days to weeks. Nothing you do makes that instant.

Turning them off entirely

If you’d rather the URLs didn’t resolve at all, the cleanest approach is to redirect date archive requests to something useful. A small snippet on template_redirect that checks is_date() and sends a 301 to your blog index will do it, and it keeps any inbound links from landing on an error page.

Some SEO plugins offer this as a setting too, usually described as disabling date archives. Whichever route you take, check for internal links pointing at those URLs first — the classic WordPress Archives widget, and any theme sidebar calling wp_get_archives(), will keep linking to pages you just redirected. That function defaults to type => 'monthly', order => 'DESC', post_type => 'post', and no post counts, and it’s the single most common source of internal links into date archives.

OPEN SOURCE

Archives, taxonomies and other generated pages are exactly where structured data tends to go missing, because they aren’t individual posts. amplifi.schema handles that with URL-pattern rules, so an archive you decide to keep can carry proper JSON-LD instead of nothing. It’s free, MIT licensed, and ships inside the combined amplifi.plugins suite.

View on GitHub

An Audit You Can Run This Afternoon

You don’t need tooling for this. Half an hour and a browser gets you most of the way.

Find out what’s actually indexed

Start in Google Search Console rather than guessing. The Pages report under Indexing shows which of your URLs are indexed and which were excluded, and you can search the URL list for date-shaped patterns. A site: search on your domain gives you a rough second opinion — it’s an officially documented operator, but Google warns in the same documentation that it doesn’t necessarily return every URL indexed under a prefix, so treat it as a smell test rather than an audit.

What you’re looking for is scale. A handful of date URLs in the index is noise. Several hundred, on a site with a couple of hundred posts, tells you the archives have quietly become the majority of your indexed footprint.

Check what your theme is doing

Open one of your own date archives and read it as a stranger would. Does it have a real heading or “Month: March 2019”? Does it carry a canonical tag, and does that tag point at itself or somewhere odd? Is it paginated sensibly? Are there internal links pointing into it from your sidebar or footer?

Then check whether your SEO plugin already has an opinion. Many install with sensible archive defaults, and it’s common to discover date archives were noindexed years ago and this whole exercise is a five-minute confirmation rather than a project.

Fix, then actually verify

Whatever you change, confirm it landed on the live page rather than trusting the settings screen. Load a date archive and view source: a noindex should appear as <meta name="robots" content="noindex, follow" /> or similar in the head. If you redirected instead, check that the request returns a 301 and lands somewhere sensible.

Then leave it alone. Re-crawling takes weeks, and the temptation to keep fiddling because the numbers haven’t moved yet has broken more sites than date archives ever have.

Frequently Asked Questions

Yes. WordPress builds the date permalink structure separately from your post permalink structure. Core looks for one of three accepted date orderings in your permalink setting and, finding none, falls back to a default of year/month/day. The only case where date archives are not generated is an empty permalink structure, meaning Plain permalinks. Append /2019/03/ to your domain to see for yourself.

Generally no. Google’s guidance on consolidating duplicate URLs describes selecting a canonical version and consolidating signals rather than applying a penalty. The genuine costs are different: ranking signals split across several near-identical URLs, and crawl effort spent on listing pages that rarely rank. Those are worth fixing on their own merits, without inventing a penalty.

Not if your goal is removing them from the index. Google’s documentation on blocking indexing states that a page must not be blocked by robots.txt and has to be otherwise accessible to the crawler, or the noindex directive is never seen. Combining a robots.txt block with a noindex tag is self-defeating. Use noindex and leave the URLs crawlable.

Not in the core sitemap. WordPress has generated an XML sitemap automatically since version 5.5, and it registers three default providers: posts, taxonomies and users. Date archives have no provider, so they are not included by default, although the sitemap system is extensible and a plugin can register its own provider. Search engines can still discover them through internal links and ordinary crawling, since a sitemap has never been the only route in.

That is deliberate collision avoidance in core. If the post ID tag appears among the first three tokens of your permalink structure, WordPress prefixes the date structure with a date segment so numeric post IDs and numeric years cannot compete for the same URL space. Sites using the Numeric permalink preset commonly see this. Nothing is broken.

It depends on how often crawlers revisit those URLs, which is typically days to weeks rather than hours. Search engines have to re-crawl each page, see the noindex directive, and drop it. There is no way to force this to happen immediately, so make the change, verify it on the live page, and leave it alone.

If you’ve never looked at what your site publishes without asking you, the date archives are a good place to start — and the audit above takes an afternoon.

Built by amplifi.studio — see also Your Admin Username Is Hiding in Plain Sight: A WordPress Author Archives Guide.