=== M 404 Handler ===
Contributors: majevski
Author: Vitold Majevski
Author URI: https://majevski.com
Plugin URI: https://majevski.com/m-404-handler
Tags: redirect, 301, 404, redirection, broken links
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.2.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Custom redirects (301, 302, 303, 307, 308) with exact, prefix or regex matching, automatic 404 redirection, 404 logging and a smart custom 404 page.

== Description ==

M 404 Handler is a universal, lightweight redirect manager and 404 toolkit:

* **Unlimited custom redirects** with your choice of redirect type: 301, 302, 303, 307 or 308.
* **Three match modes** — exact path, path prefix (with optional remainder passthrough) or full regular expressions with `$1` capture-group substitution.
* **Enable or disable** individual redirects without deleting them.
* **Hit counter and last-hit timestamp** on every redirect, so you know what is actually being used.
* **Full-featured management table** with search, status/code/match filters, bulk actions (enable, disable, delete, reset counters) and pagination.
* **Automatic 404 redirection** — send every remaining 404 to any page or URL you choose, with the status code you choose.
* **404 error log** — every broken link is recorded, aggregated per URL with a hit counter, referrer, user agent and (optionally anonymized) IP. Create a redirect from a log entry in one click.
* **Smart custom 404 page** — serve any WordPress page as your 404 response with a real 404 status (search engines will not index it), plus optional "Did you mean…?" suggestions based on similar slugs. Use the `[m404_suggestions]` shortcode to position the suggestion list.
* **Privacy-friendly logging** — IP anonymization on by default, configurable retention with automatic daily cleanup, and ignore patterns to keep bot probes out of the log.

The plugin is built to be as light as possible: no frontend assets are ever loaded, redirects are resolved with a single indexed database query before WordPress runs its main query, and sites without rules of a given type skip matching for that type entirely.

== Installation ==

1. Upload the plugin folder to `/wp-content/plugins/`, or install it through the WordPress Plugins screen.
2. Activate the plugin through the Plugins screen. The database tables are created automatically.
3. Go to **404 Handler → Redirects** to add your first redirect, and **404 Handler → Settings** to configure the automatic 404 fallback, the custom 404 page and logging.

That is all — sensible defaults are in place out of the box (logging on, 30-day retention, IP anonymization on, no automatic redirection until you enable it).

== Frequently Asked Questions ==

= What is the difference between the redirect types? =

* **301** Moved Permanently — the standard for permanent moves, passes SEO value.
* **302** Found — a temporary redirect.
* **303** See Other — the response to a form submission that should be fetched with GET.
* **307** Temporary Redirect — like 302, but the request method is preserved.
* **308** Permanent Redirect — like 301, but the request method is preserved.

= How do regular expression redirects work? =

Enter the pattern without delimiters as the source, e.g. `^/old-blog/([0-9]+)/(.+)$`, and use `$1`, `$2` … in the target, e.g. `/blog/$2`. Patterns are validated when you save and can never break your site: a pattern that fails at runtime simply does not match.

= Do redirects work with query strings? =

Matching is done on the path. When "Pass the original query string" is enabled (the default), the query string of the incoming request is appended to the target URL.

= Why do cached pages not get redirected or logged? =

If a full-page cache or CDN serves a cached copy of a 404 page, WordPress (and therefore this plugin) never runs. The plugin sends no-cache headers with every 404 response it generates, but if your CDN is set to "cache everything", exclude 404 responses from caching.

= Is the plugin GDPR-friendly? =

Yes. IP anonymization is enabled by default, the log can be disabled entirely, and old entries are purged automatically after the retention period you configure.

= Will I lose my redirects if I delete or reinstall the plugin? =

No. Since 1.2.0, deleting the plugin from the Plugins screen removes only its files — your redirects, 404 log and settings stay in the database and reappear the moment you reinstall and activate the plugin. If you want a truly complete removal, enable "Delete all data on uninstall" under 404 Handler → Settings → Data removal first, then delete the plugin.

= How do I translate the plugin? =

The text domain is `m-404-handler`; a POT file ships in `/languages`. Use Loco Translate, Poedit or translate.wordpress.org language packs.

= Where do I get support? =

Write to pagalba@majevski.com or visit https://majevski.com — support is available in English and Lithuanian.

== Changelog ==

= 1.2.0 =
* Fixed: deleting the plugin no longer destroys your data. Redirect rules and the 404 log are user content — but uninstalling used to drop both database tables and every setting, so the common "delete the old version, upload the new one" way of updating manually wiped every redirect on the site. Uninstalling now removes only the plugin's files; all data survives and reappears on reinstall. A new opt-in setting (Settings → Data removal) restores the old complete-cleanup behaviour for those who really want it.
* Fixed: an update can no longer silently deactivate the plugin. When an update archive's folder name differed from the folder the plugin was installed in, WordPress installed the new version under the new name — the active copy vanished, every redirect stopped working, and the plugin's Settings and Redirects links disappeared from the Plugins screen without any error. Updates are now always installed into the plugin's current folder, whatever the archive is named.
* Fixed: re-activating the plugin now recounts the stored redirect rules instead of assuming there are none. Previously, activation could record "zero rules" while the rules were still in the database, leaving every existing redirect visible in the admin but inactive on the site.

= 1.1.2 =
* Fixed: an update that could not be downloaded now says why. WordPress gives a package download five minutes to finish, which is longer than any host allows a single admin request to run, so when the connection to majevski.com stalled the request was cut off before WordPress could report anything and the Plugins screen showed only the browser's own "Connection lost or the server is busy". Downloads of this plugin's releases are now capped at sixty seconds — far more than a release archive needs, and short enough that the real transport error is reported on the screen instead of the request dying silently.
* New: a Site Health check under Tools → Site Health → Status, "M 404 Handler can reach its update server". It makes the same two calls the installer makes — the release manifest, then the package itself — and prints the exact error when either fails, so a blocked outbound connection is named as such instead of appearing as a failed update.

= 1.1.1 =
* Fixed: new releases now appear on the Plugins screen within an hour instead of up to a day. WordPress only re-checks for updates every 12 hours by default and the release manifest was cached for another 12 — visiting the Plugins screen now forces a fresh check (at most hourly), and the manifest cache lasts one hour.
* New: a "Check for updates" link in this plugin's row on the Plugins screen — click it and, if a newer release exists, the native "update now" link is active immediately.

= 1.1.0 =
* New: automatic update checks against majevski.com. When a newer release is published, WordPress shows its standard update prompt with one-click install; a "View details" popup shows the changelog. The update channel is pinned to https://majevski.com over HTTPS, checks are cached and fail open, and equal or older versions are never offered.

= 1.0.0 =
* Initial release: custom redirects (301/302/303/307/308) with exact, prefix and regex matching, hit counters, full management table, automatic 404 redirection, aggregated 404 logging with retention, smart custom 404 page with suggestions.

== Upgrade Notice ==

= 1.2.0 =
Important data-safety release: uninstalling no longer deletes your redirects and 404 log (now opt-in), updates can no longer silently deactivate the plugin by changing its folder name, and re-activation correctly restores existing rules. Update through the Plugins screen — never delete the plugin to update it.

= 1.1.0 =
Adds self-hosted update checks: from this version on, new releases appear in the normal WordPress update flow. Install this version manually once — older versions do not know about the update channel yet.

