Installation
MevvLegal needs WordPress 6.0 or newer and PHP 8.1 or newer. There is no other requirement and no external library.
- Upload and activate the plugin. A table for the anonymous consent log is created on activation.
- Open MevvLegal → Kurulum Sihirbazı and walk the five steps: your institution details, the categories and banner text, a front-end scan, the hold suggestions from that scan, and the legal pages.
- Check the result under Ön Yüz Denetimi. The number you are aiming for is “onaysız çalışan üçüncü taraf betik: 0”.
Two optional integrations: the WP Consent API plugin, if you use plugins that speak it, and WPML or Polylang for translating the banner title and text.
Pro is a separate plugin installed alongside the free one, and it declares the free plugin as a hard requirement. With Pro absent, MevvLegal never contacts our servers at all.
How it works
Most cookie banners ask the question and then load the tracking code regardless of the answer. The visitor said no and was tracked anyway.
- Before consent, every tracking script on the page is rewritten so the browser will not parse, download or run it. Its address is moved aside, not deleted — consent has to be able to put it back.
- The banner is printed to every visitor, identical bytes, and starts hidden. The script in the browser decides whether to show it.
- When the visitor makes a choice, the decision is written to their own browser and the scripts for the categories they accepted are released.
- The decision is tied to the exact text they were shown. Change the categories, the banner text or the cookie inventory and old consents stop counting — the question is asked again.
- If they later withdraw consent, the cookies named in your inventory are deleted where that is possible, and the page reloads so nothing that was already running keeps running.
The server never varies the page per visitor: no cookie is read, no session is checked, no login state is inspected on the front end. A banner that changes the HTML per visitor breaks full-page caching — and behind such a cache, the first visitor’s page gets served to everybody, silently.
If the script fails to load entirely, the banner stays invisible and the held scripts are never released. The failure mode is “nothing is tracked”, not “everything is tracked”.
Holding scripts back
Three mechanisms, because tracking code reaches a WordPress page in three different ways.
- Scripts queued by WordPress
- Mapped by their handle to a category on the Bekletilen betikler list. MevvLegal rewrites the tag so the browser treats it as plain text and moves the address out of
src. - Tags you write yourself
- Write the contract by hand: give the script
type="text/plain"and a category attribute, or put an iframe’s address in the held attribute instead ofsrc. - Tags other plugins echo directly
- Some plugins print their tracker straight into the page, where the normal mechanism cannot see it. Those are matched by a fragment of their address instead, on the Başka eklentilerin bastığı etiketler list.
The third case is not hypothetical. On a live store, Google Listings & Ads was printing the Google Ads tag at a priority no filter can reach, and unconsented marketing cookies were being written under a banner that believed it had blocked them.
Releasing a held script requires replacing the element, not just editing it: a script the browser has already parsed as plain text will never run again, no matter what you do to its attributes. Only a brand-new node executes. Non-script elements — iframes, images — just get their address back.
While that third list is empty, no page is buffered at all. Output buffering alongside another plugin is one of the most commonly reported causes of a white screen, so it is only switched on when there is actually something to catch.
MevvLegal also repairs its own work. A tag whose address looks parked but which lost its category attribute is one that another plugin rewrote after us — the browser would run it. Those are detected, re-held, and counted on the audit screen as Yarım kalmış (UYARI).
The banner
Settings live under MevvLegal → Çerez Onayı, which is one long form rather than a set of tabs.
- Bant
- Whether it shows, its title, its text, and whether it sits at the top or the bottom. Changing the text changes the policy version and re-asks everyone — that is the point, not a side effect.
- Görünüm
- Six values: banner background, text colour, button colour, button text colour, corner radius and maximum width. Contrast below the WCAG threshold produces a warning but does not block saving.
- Kategoriler
- Necessary (always on, cannot be switched off), preferences, statistics, marketing.
- Cookie settings link
- A shortcode you can put in your footer so a visitor can reopen the banner and change their mind.
Reject is as easy as accept. Both buttons carry the same class and reject is printed first. There is one colour setting, applied to both — you cannot make the reject button quieter than the accept button. Consent obtained by making refusal harder is not freely given.
The banner is a dialog but it does not lock the page behind it and it does not trap focus. Escape closes the detail panel, never the banner itself: treating indecision as either an acceptance or a refusal would be wrong in both directions.
The cookie inventory
The inventory is one list per category, a row per cookie: name, where it lives, who provides it, how long it lasts and what it is for. Everything else is built on it — the cookie policy page is generated from it, and it is the list that says what may be deleted when consent is withdrawn.
MevvLegal ships no default rows. No pre-filled Google Analytics, no pre-filled Meta Pixel. Declaring a processing operation you do not actually run is worse than declaring too little.
You do not have to write it from memory. Çerez gözlemi measures which cookie names appear in real visitors’ browsers — a sample of them, at a rate you choose — and offers them as suggestions.
Only names are ever read. The value of a cookie is never read, never sent and never stored. And a discovered name is never written into your inventory on its own: provider, duration and purpose are things only you know, and inventing them would put a fabricated declaration on your own policy page.
Google Consent Mode v2
Off by default, and switching it on is a deliberate trade you should understand before you make it.
With it on, four signals are sent — advertising storage, analytics storage, ad user data and ad personalisation — set to denied before any choice is made and updated when the visitor decides. Statistics maps to analytics storage; marketing maps to the three advertising signals. Preferences and necessary map to nothing, because sending a signal that changes no tag behaviour would suggest a control that does nothing.
What it costs, stated plainly: with this option on, the Google tag runs on your page. Even when the visitor rejects everything, a request goes to Google, and their IP address and the page address go with it. No cookie is written — all four signals say denied — but the request happens. Your front-end audit can no longer say “unconsented third-party scripts: 0”.
What it buys is Google’s conversion modelling, which needs the “denied” signal in order to exist. It is a genuine choice between two defensible positions, which is why it is offered rather than switched on quietly.
The exemption is narrow: only the Google tag loaders themselves are released. Meta Pixel, TikTok, Criteo and the Google advertising domains stay held.
Turning this on does not invalidate anyone’s existing consent — it is deliberately left out of the policy version hash.
Regional rules and GPC
Off by default. When on, three buckets can behave differently.
- EU/EEA, the United Kingdom and Turkey
- Opt-in, always. This is not a setting and cannot be loosened.
- United States
- Opt-in, or opt-out — scripts run and the banner says you may refuse.
- Everywhere else, and visitors whose region cannot be determined
- Opt-in, or free — scripts run and the banner never appears.
The region is determined from the browser’s time zone. No IP lookup, no country header, no geolocation service, nothing written to the visitor’s device. The common approach is to send the visitor’s IP to a third-party lookup service — which is itself a disclosure, and in at least one competing product it happens over plain HTTP.
That choice has an honest cost: a time zone is not a country. US states cannot be told apart, so there is no state-level rule. And every unknown case — an empty value, UTC, an unrecognised name — falls into the opt-in bucket. The opposite default would write unconsented marketing cookies for an EU visitor whose browser simply did not report a zone, and it would never show an error anywhere.
Global Privacy Control is always honoured and has no setting. When a browser sends it, an opt-out or free bucket is tightened back to opt-in. It only ever tightens; it never releases a script anywhere.
GPC is never turned into a consent record. The banner is not closed and nothing is stored. A browser setting is not informed consent — and closing the banner would take away that visitor’s ability to accept. Do Not Track is deliberately not honoured: some browsers sent it by default without the user choosing, and treating a default nobody picked as an objection is the mirror image of treating silence as consent.
In an opt-out or free bucket no consent record is written and nothing is sent to the log. Writing one would turn the record that has to prove consent into a fabricated statement. A decision the visitor actually made always beats the regional rule.
Deleting cookies when consent is withdrawn
There is no setting for this. When a visitor withdraws consent, the names in your inventory are removed where the browser allows it — cookies, local storage and session storage, across every path and domain candidate. A trailing asterisk works as a wildcard. Deletion runs before the page reloads, because after the reload nothing is guaranteed to run.
The necessary category is never touched, and neither is the visitor’s own consent record.
Two kinds of cookie cannot be deleted from a browser, and no plugin can change that. HttpOnly cookies are invisible to JavaScript by definition: they never appear at all, so an expired cookie cannot be written over them. And cookies set on another domain — doubleclick.net, facebook.com — cannot be removed by a script running on your site; the browser’s domain separation forbids it. That is most third-party marketing cookies.
So MevvLegal does not say “we deleted them all”. The script reports what it tried and what is still there, and the admin screen prints the limit as it is. Claiming something you did not measure is the most expensive kind of mistake in a product like this.
The real answer for third-party cookies is not deleting them but never letting them be written: that is what script holding is for. Deletion complements it; it does not replace it. And only names written in your inventory can be deleted — if the inventory of a non-necessary category is empty, withdrawing consent will stop the scripts but leave the cookies already on the device.
The consent record
Two halves, deliberately separated.
- On the visitor’s device
- Their decision, the policy version it applies to and when it was made. Stored in their own browser, not in a cookie.
- On your server
- Counters only: for each day, each policy version and each combination of choices, how many times it occurred. No IP address, no visitor id, no user id, no per-person row. Alongside them, an archive of every policy text that was ever shown.
Person-level proof of consent would mean giving every visitor a durable identity — opening a brand new processing operation, with no consent of its own, in order to prove consent for the old one. This product does not do that, and does not pretend to.
The honest limit is printed on the screen, in the export, and in the source: the counters are not unique per person and can be inflated by a malicious request. This is not a visitor counter. It is a record of how many consent statements were given against which text.
Onay Analitiği reads those counters: full acceptance, partial acceptance and full rejection rates, a per-category breakdown, a comparison between policy versions and a daily trend. Pro adds a CSV export that carries the counters, the policy texts and that honest-limit sentence as its last row.
Legal pages
Six templates, generated from your own details and your own inventory:
- Free
- Gizlilik Politikası ve KVKK Aydınlatma Metni · Çerez Politikası
- Pro
- Kullanım Koşulları · Mesafeli Satış Sözleşmesi · İade ve Değişim Koşulları (Cayma Hakkı) · Teslimat ve Kargo Koşulları
The cookie policy is free on purpose: a banner without one is meaningless.
Turkish and English are available. The English texts are not translations — they are separate texts written against GDPR, the ePrivacy Directive and the Consumer Rights Directive, because the Turkish ones are written against KVKK and the Turkish distance sales regulation. There is no English Mesafeli Satış Sözleşmesi, and the screen explains why: it is a document mandated by Turkish regulation with no equivalent obligation in EU law.
Clauses that cannot be known are left in square brackets rather than filled in — governing law, competent court, an EU representative, retention periods, who pays return shipping. A clause filled in by guesswork is worse than a clause left blank.
Generated pages are ordinary WordPress pages. No custom post type, no shortcode wrapper, no lock — you can edit them, and they survive the plugin being removed. Overwriting one is always an explicit choice and always saves a revision first.
The screen tells you when a published page has drifted from what would be generated today, and Farkı göster shows the difference line by line. It also checks whether your published cookie policy still matches your open categories.
These texts are not legal advice, and every screen in the plugin says so.
Data subject requests
A shortcode puts a request form on any page. The visitor gives their email address and chooses whether they want a copy of their data or its deletion. Neither option is pre-selected — pre-ticking one would nudge them away from erasure.
- The form is submitted. It is open to visitors who are not logged in, because the right does not depend on membership.
- WordPress emails them a confirmation link. Nothing happens until they click it.
- You fulfil the request from Tools → Export Personal Data or Erase Personal Data, WordPress’s own screens. A counter on the MevvLegal screen shows how many confirmed requests are waiting.
The response to the visitor is always the same sentence, whatever happened — including when the address is not on file. Telling them otherwise would disclose whether an address is registered with you.
The export and the erasure are done by WordPress core, not by MevvLegal. MevvLegal registers no exporter and no eraser of its own, because it stores no personal data — an empty section in the export would be noise, not information.
The form has no nonce, deliberately: a nonce dies behind a full-page cache and takes the form with it, silently. A rights surface that quietly stops working is not an acceptable failure here. Abuse is handled by a honeypot field, a site-wide hourly cap, and WordPress’s own duplicate protection.
The form carries the caveat that records under a legal retention obligation — invoices, commercial books — may be kept until that period ends, and that you will be told which records and why.
The front-end audit
Ön Yüz Denetimi fetches your own home page and counts what is actually on it: how many script tags, how many are held, how many are running free and third-party, how many are inline, how many are data blocks that are not scripts at all, and the same for iframes.
The line that matters is Serbest ve ÜÇÜNCÜ TARAF. When it reaches zero the screen says so: onaysız çalışan üçüncü taraf betik: 0. That number is the product’s actual promise.
From the same screen you can turn recognised trackers into hold rules, and write discovered cookie names into your inventory.
Nothing is activated automatically. Holding the wrong script breaks a site silently — a payment provider’s 3-D Secure script, a shipping integration, live chat: the page opens, the button does nothing. So suggestions are proposed and you approve them. And removing a hold rule is free and unconditional in every edition, licence or no licence.
Each scan bypasses your page cache. Without that the screen would be reading HTML generated hours ago and telling you it is the current state.
Free and Pro — the rule that never bends
This plugin has one invariant, written at the top of its licence class:
The cookie banner and script holding are never tied to the licence. Here the rule is harder than in our other products. Elsewhere a lapsed licence costs you appearance; here it would cost you compliance. If the banner went quiet on a site whose licence expired, that site would start writing analytics and marketing cookies to its visitors without permission — and the fine lands on the data controller, not on the plugin vendor. A product that leaves its customer legally exposed over a payment problem is not a product that can be sold.
What that means in practice: only new configuration is restricted. Defining new categories, generating new legal templates, adding rows to the cookie table. Configuration that already exists keeps working exactly as it is.
Free, permanently, with no licence check anywhere on the front-end path:
- The cookie banner, and its colour, radius and width settings
- Script holding — all three mechanisms
- The consent record in the visitor’s browser, and the anonymous consent log
- The consent analytics screen
- The front-end audit, cookie observation, and turning a recognised tracker into a hold rule
- Removing a hold rule, and switching Consent Mode or the regional rule off — unconditionally
- Deleting cookies when consent is withdrawn
- The WP Consent API bridge, and GPC
- The data subject request form
- The setup wizard, the text diff, and multilingual banner text
- The privacy and cookie policy templates, and editing any generated page
Pro adds:
- Editing the cookie inventory, the script mapping and the raw-tag mapping
- The four e-commerce templates: terms of use, distance sales contract, returns and withdrawal, delivery and shipping
- Turning Consent Mode on, and turning the regional rule on
- CSV export of the consent log
When a licence expires:
- Your compliance is unaffected. The banner, script holding and the consent record keep working.
- Your cookie inventory is kept, not cleared. Emptying it would make your published policy wrong overnight.
- Existing hold rules keep holding. The inventory fields simply become read-only.
- Legal pages you already generated stay published and stay editable.
- Consent Mode and the regional rule, if they were already on, stay on. Only turning them on is gated — turning them off, and tightening a bucket back to opt-in, are always available.
If our servers are unreachable, an active licence keeps working for seven days on the strength of the last successful check — but only if it had ever verified successfully.
When something does not work
- The audit shows “Yarım kalmış (UYARI)”
- Another plugin is rewriting the same script tag after MevvLegal and stripping the hold. This is measured, not theoretical — a hosting assistant plugin was doing exactly this on a live site. MevvLegal filters as late as it possibly can and repairs what it can detect. If a row persists, deactivate the plugin that queues that script and scan again.
- A tracker is running even though the banner is installed
- It is being echoed straight into the page rather than queued. Add a fragment of its address to Başka eklentilerin bastığı etiketler — that list is what catches those.
- The audit screen shows a configuration that no longer exists
- It should not: each scan bypasses the page cache. If the front end still looks stale to visitors, purge your full-page cache — on LiteSpeed,
wp litespeed-purge all. - The regional rule does not seem to apply
- A cache or optimisation plugin is deferring and reordering scripts, so the region decision arrives after the banner has already decided. The product re-applies the rule when the region script loads; if it still fails, stop deferring MevvLegal’s scripts.
- The banner does not appear at all
- The audit says Bant sayfada bulunamadı when it is missing. Most often the theme never calls the standard body-open hook — there is a footer fallback for that. Remember that when the banner is not printed, held scripts are never released either.
- A white screen after enabling raw-tag mapping
- That list is the only thing that switches on output buffering. Buffering alongside another plugin that also buffers is a known cause. Empty the list to confirm, then add addresses back one at a time.
- The consent log stays empty
- The table is created on activation, and activation does not run on plugin updates. It is re-checked on every admin page load, so visiting the admin once is usually enough. Also confirm the log is switched on.
- A button or a payment step stopped working after adding a hold rule
- You held something that is not a tracker. Remove the rule — removal is free, unconditional and immediate.
- Consent Mode is on and the audit no longer reports zero
- That is expected and is stated on the setting itself: with Consent Mode on, the Google tag runs.
Limits
- HttpOnly cookies and cookies set on another domain cannot be deleted from a browser. No plugin can change this.
- Only names written in your inventory are deleted. An empty inventory means nothing is removed.
- Cookie observation reads names, never values. It also never writes a discovered cookie into your inventory by itself, and never invents its provider, duration or purpose.
- Hold rules are never enabled automatically, and no category is suggested for an unrecognised third party.
- Regions come from the browser’s time zone. A time zone is not a country, US states cannot be distinguished, and every unknown case falls to opt-in.
- The EU/UK/Turkey bucket cannot be loosened. There is no setting for it.
- GPC cannot be switched off, and Do Not Track is not honoured.
- No person-level proof of consent. The counters are anonymous, not unique per person, and can be inflated.
- The log cannot tell you which language a consent was given in.
- MevvLegal writes no export or erasure engine of its own. WordPress core does that work.
- Under Consent Mode only the Google tag loaders are exempted. Nothing else is released.
- The audit fetches your home page over HTTP. It does not run a headless browser on your server, so it sees what that one page prints.
- Legal texts are static. They are correct today; when the law changes they do not update themselves. The plugin tells you when a published page has drifted from the template, but it cannot tell you when the template itself has aged.
- These texts are not legal advice.
- Nothing is deleted when the plugin is uninstalled — the consent log table and your settings stay where they are.
And the one that matters most: no plugin can make you compliant. This one holds scripts, records consent and generates pages from what you told it. What you told it is still your responsibility, and the wizard’s last screen says exactly that instead of congratulating you.