WordPress, 404s, and the Cost of a Typo
A request arrives at a WordPress site. The URL does not exist. At that point, you might expect the web server to reject it and move on. Instead, WordPress can do a surprising amount of work before it can confidently say:
404. Nothing here.
That always bothered me. Not because 404 pages are unusual. They are part of the web. What bothers me is the amount of machinery that may be involved in producing an answer that, in many cases, could have been known much earlier. And there is a second problem hiding behind the first one. Sometimes the requested resource actually does exist. Only the URL is slightly wrong. One missing letter can be enough to turn perfectly valid intent into a dead end. That combination made me wonder whether WordPress handles some requests too late.
A 404 is not always a cheap answer
WordPress is an application, not a static file server. That distinction matters. When a request reaches WordPress, the application needs context before it can decide what the request means. Routing happens. Rewrite rules are evaluated. The request is parsed. Plugins may load. The theme may become involved. Database queries may happen.
Eventually WordPress reaches a conclusion:
- There is no matching resource.
- The response becomes a 404.
- From the visitor's perspective, that sounds simple.
- From the server's perspective, it may not have been simple at all.
This becomes especially wasteful when the request was obviously useless from the beginning. Think about automated probes, malformed URLs, abandoned paths, random scanner traffic, old links, broken bots and requests for resources that have never existed on the site. The application spends resources discovering the absence of something. That feels backwards.
If the answer can already be established before the CMS is fully involved, why boot the CMS just to confirm it?
Then there is the opposite problem
Rejecting useless requests early sounds straightforward. But there is a trap. Not every incorrect URL represents incorrect intent. Assume this is a valid page:
Code:
/example-title/
Now somebody links to:
Code:
/exampl-title/
The visitor's intention may still be obvious to a human. The resource still exists. But the path no longer identifies it reliably. If WordPress cannot determine which resource was intended, the request ends in a 404. And that exposes another form of waste. The first kind wastes server resources. The second kind wastes traffic. A visitor, search engine, external website, bookmark, email or automated system may be trying to reach a real resource. A damaged or outdated path can break that connection completely. Simply rejecting unknown requests earlier would therefore solve only half of the problem. Some requests should be rejected. Some should be recovered.
The problem is identity
A typical WordPress permalink can look like this:
Code:
/example-title/
It is nice to read.
It tells humans something about the resource. But it also has to identify the resource. Those are two different jobs. Preflight separates them.
For a Post, a Preflight URL can look like this:
Code:
/example-title/:-:123/
Code:
example-title
Code:
:-:
Code:
123
What happens when somebody makes a typo?
Take the canonical Preflight URL:
Code:
/example-title/:-:123/
Code:
/exampl-title/:-:123/
But the resource identity is still there.
Code:
123
It can identify resource 123 directly.
It can then compare the requested path with the current canonical path and see that they do not match. The result can be a permanent redirect:
Code:
301 -> /example-title/:-:123/
It also works when the URL simply gets old
Typos are only the obvious example. Imagine the resource title or slug changes later.
The old URL might be:
Code:
/my-old-title/:-:123/
Code:
/my-new-title/:-:123/
That means an old external link can still carry enough information to identify the intended resource and reach its current canonical URL. The human-readable part of the URL is allowed to evolve without becoming the only source of truth about what the URL points to. This is a fairly old idea in computing. What interested me was applying it to WordPress not only for URL resilience, but also for request qualification.
Stable identity makes earlier decisions possible
Once public resources have an identity that can be recognized without relying entirely on WordPress resolving an arbitrary semantic path, another possibility appears. You can ask useful questions earlier.
- Is this a known resource?
- Does it still exist?
- Does the requested semantic path match its canonical path?
- Is this request valid enough to justify handing it to WordPress?
- Or is there already enough information to produce the correct response?
That is where the performance side and the URL side of Preflight meet. The resource ID is not there merely to make unusual-looking URLs. It provides a deterministic piece of information that can be evaluated before the normal WordPress request lifecycle has done all of its work.
That became Preflight
I called the experiment Preflight because that is essentially what it does. Before a request is handed to the full WordPress application, perform a lightweight qualification.
- Not a replacement router.
- Not another cache.
- Not a second CMS.
- Just an early decision layer in front of the expensive one.
A valid request can continue normally. A known resource with an incorrect or stale semantic path can be redirected to its canonical URL. A resource known to be gone can be handled accordingly. An invalid request can be rejected early. And when Preflight cannot make a reliable decision, WordPress remains the fallback.
That last point matters.
Early handling only makes sense when uncertainty does not break normal application behavior. Preflight should act when it knows. Not when it guesses.
There is another expensive corner: missing static files
There is a related case that is easy to overlook. A missing image, CSS file, JavaScript file or font can also fall through the normal WordPress rewrite path on some setups. At that point the server may end up starting WordPress merely to discover that a static file does not exist. That is an impressive amount of software to answer a very small question.
Preflight treats static files differently from resources such as Posts, Terms and Authors. They do not need the same identity model. Where the server configuration allows it, missing static resources can be finished at the web-server level instead of falling through to the normal WordPress front controller.
Again, the principle is the same: Do not start the application when the answer does not require the application.
WordPress is not doing anything irrational
Its architecture makes sense within the assumptions it was built around. The application receives a request and figures out what that request means. The question is whether every request reaching a modern public WordPress installation still deserves that treatment.
- Bots probe arbitrary URLs.
- Scanners try common files.
- Old paths survive for years.
- Links get copied incorrectly.
- Automated systems invent paths.
- Humans make typos.
- Removed images remain referenced somewhere.
Yet many of those requests can enter the same application pipeline as a legitimate page view. That is the part I find difficult to justify. The application should be involved when application knowledge is required.
But when the answer is already known, executing the whole stack looks less like flexibility and more like unnecessary work.
Not every optimization has to optimize execution
This is probably the main thing I took away from building Preflight. Performance discussions tend to focus on execution.
- How quickly can PHP run?
- How much can we cache?
- How many queries can we remove?
- How small can the page become?
- Those are useful questions.
But there is another one:
Should this request cause application execution at all?
- Sometimes the best query is the one that never runs.
- Sometimes the fastest plugin is the one that never loads.
- Sometimes the cheapest 404 is the one WordPress never has to discover.
- And sometimes a broken-looking URL should not be a 404 at all.
If the stable identity still tells us which resource was intended, throwing that request away would be just as wasteful as processing an obviously invalid one through the entire application stack.
That is the idea behind Preflight.
Reject what is clearly invalid.
Recover what is still identifiable.
And involve WordPress when WordPress is actually needed.
https://wordpress.org/plugins/imedes-preflight/
Last edited: