# wppoland.com, full markdown digest > Curated long-form Markdown for retrieval-augmented LLM agents. Includes the canonical EN service pillars and top EN blog posts. For the short index see /llms.txt. For other locales, fetch any URL with `Accept: text/markdown` content negotiation. Source of truth: https://github.com/wppoland/wppoland-new Generated at build time, 61 pillars + 60 blog posts. --- # Service pillars ## AI-built website rescue and remediation URL: https://wppoland.com/en/ai-built-website-rescue/ Description: Audit and repair for WordPress and WooCommerce sites built or heavily modified by AI. Senior review of vibe-coded plugins, security holes, broken flows, and AI-slop content. Published: Sat Jun 20 Updated: Fri Jul 10 Type: guide Level: advanced ## What AI-built WordPress site rescue covers
AI can build a WordPress or WooCommerce site fast. It cannot take responsibility when that site leaks data, breaks checkout, or quietly fills Google with duplicate pages. This service is the senior cleanup after the AI: we audit what was generated, find what is unsafe or broken, and fix it, with a human accountable for every change.
This is not the same as our general [WordPress repair and technical support](/en/wordpress-repair-service-technical-support/) or a standard [WordPress security audit](/en/wordpress-security-audit/). Those assume a site built by people. Here the failure patterns are specific to generated code and content, and the remediation is different. ## How AI-built sites tend to break The damage clusters into a few recognisable patterns. A typical case is a WooCommerce store where an AI-generated checkout customisation skipped nonce verification, so the cart could be manipulated through a forged request, and nobody noticed until chargebacks started. Another is a marketing site where an assistant generated forty near-identical service pages that compete for the same query, so none of them rank and the whole domain looks thin to Google. Other recurring failures: - Generated PHP that calls functions that do not exist, or that were hallucinated from a different plugin's API. - Admin-ajax and REST endpoints registered without a capability or nonce check. - Unsanitised form input written straight into the database or echoed back into the page. - Plugin sprawl: ten plugins installed to solve a problem one line of code would have handled, dragging Time to First Byte over a second. - Content with confident but wrong facts, fabricated statistics, and invented client names. - Migrations the AI "finished" that silently dropped redirects, breaking indexed URLs. ## Symptoms of a broken AI-built site and their causes Not every broken AI-built site needs the same response. The symptom usually points to whether a targeted rescue is enough or the foundation should be replaced. Use this table as a first-pass triage before you commit budget or downtime. | Symptom | Likely cause | First action | | --- | --- | --- | | Checkout or cart fails intermittently | AI-wired WooCommerce hooks, missing nonce on cart mutations | Pause paid traffic; run a functional audit on the checkout path | | Subscriber or anonymous user can trigger admin AJAX | Missing `current_user_can()` on a `wp_ajax` handler | Security pass on generated PHP; see [auditing AI-generated plugin code](/en/auditing-ai-generated-wordpress-plugin-code/) | | Scanner flags critical CVEs on common plugins | Outdated versions installed during a fast AI-assisted build | Run a [WordPress security audit](/en/wordpress-security-audit/) and patch or remove exposed plugins | | Forty near-identical service pages, none rank | AI content cannibalisation and thin duplication | Content inventory; consolidate or noindex duplicates | | White screen or fatal error after a small change | Hallucinated function calls in generated custom code | Code inventory against developer.wordpress.org; scope rescue vs rewrite | | Time to First Byte above one second on a simple page | Plugin sprawl from "install a plugin for that" answers | Performance strip-down; measure before adding more tooling | | Form saves but stored data looks corrupted or executable | Unsanitised `$_POST` written straight to options or post meta | Plugin code audit; treat as active XSS or injection risk | | REST or admin-ajax endpoint returns data it should not | Missing `permission_callback` or nonce on custom routes | Endpoint audit; block public access until fixed | | Owner wants to keep using AI in the build | No review gate, no version control on generated output | Rescue first, then guardrails; see remediation stages below | If most rows point to isolated, fixable gaps in otherwise sound WordPress core usage, rescue is usually the cheaper path. If hallucinated APIs, missing security primitives, and plugin sprawl show up together across custom code and content, plan for a partial or full rebuild and say so before repair work starts. ## What an audit of an AI-built website checks The audit inventories everything the AI touched and triages it by two axes: security risk and revenue risk. We separate the code AI wrote, the plugins it chose, and the content it produced, because each needs a different fix. You get a written split of what is safe to keep, what must be rewritten, and what should be removed, with the reasoning behind each call. ## Remediation stages for an AI-built website Remediation follows a fixed order so security and revenue risks close before cosmetic work. Stages can overlap in calendar time, but we do not skip ahead to performance tuning while checkout is still broken or a public endpoint still leaks data. | Stage | Focus | Deliverable | | --- | --- | --- | | 1. Inventory and triage | Map every AI touchpoint: custom PHP, plugin choices, content, migrations | Written keep / rewrite / remove split with reasoning | | 2. Security pass | Nonces, capabilities, sanitisation, exposed AJAX and REST routes | Patched or removed vulnerable handlers; [plugin code audit](/en/auditing-ai-generated-wordpress-plugin-code/) findings closed | | 3. Functional repair | Checkout, forms, CRM and payment integrations | Broken user flows restored and regression-checked | | 4. Content remediation | Duplicate pages, hallucinated facts, thin AI filler | Consolidated pages that can rank; corrected or removed false claims | | 5. Performance recovery | Plugin bloat, render-blocking assets, cache misconfiguration | Core Web Vitals back in range on key templates | | 6. Guardrails | Version control, staging workflow, review checklist for future AI use | Short runbook so the next prompt does not reopen the same holes | Emergency items (active exploit, broken checkout during a campaign, legal or compliance exposure) jump the queue inside stage 2 or 3 regardless of where the rest of the site sits in the table. ## Common security gaps in AI-generated code Generated WordPress code passes the "it runs" test while failing the "it is safe" test. We test directly for the gaps that matter: missing `wp_verify_nonce` and `current_user_can` checks, input that reaches the database without `sanitize_*` or prepared statements, output that skips `esc_*`, PHP files reachable without an `ABSPATH` guard, and endpoints exposed without authorisation. The failure patterns repeat often enough that we published a diagnostic walkthrough in [auditing AI-generated WordPress plugin code](/en/auditing-ai-generated-wordpress-plugin-code/). Where the security surface is large, this connects to a full [WordPress security audit](/en/wordpress-security-audit/). We documented the real CVE patterns these stacks carry in [outdated-plugin CVEs](/en/wordpress-security-audit-outdated-plugins-cve-2026/). ## How to clean up AI-generated content A site built with AI usually has an AI-content problem too. We deduplicate pages that cannibalise each other, correct hallucinated facts and round-number fake statistics, and consolidate thin pages into ones that earn citations. This is the same discipline behind [GEO and LLMO optimisation](/en/geo-llmo-optimization/): content that is accurate and distinct, not generated filler. ## How to fix performance on an AI-built website AI tends to solve problems by adding plugins. We reverse that: remove the bloat, replace plugin stacks with targeted code, and bring Core Web Vitals back into range. The dedicated [Core Web Vitals rescue for AI-built WordPress sites](/en/core-web-vitals-rescue-ai-built-wordpress/) page walks through the staged teardown, measurement checklist, and the anonymised case where median uncached TTFB dropped from 1.47 s to 0.68 s after cutting 38 plugins to 21. See [plugin sprawl after an AI build](/en/plugin-sprawl-ai-wordpress-build/) for the full diagnostic and [AI-slop content cleanup](/en/ai-slop-content-cleanup-wordpress/) when hallucinated facts need editorial triage. For a formal audit scope, the [Core Web Vitals audit](/en/services/core-web-vitals-audit/) service covers the same metrics with a written backlog. ## Should you rescue or rebuild an AI-built website After the audit you get an honest recommendation. If most of the AI output is salvageable, a targeted rescue is the cheapest path. If the foundation is unsound, we scope a rebuild instead of patching forever. When rebuild is the right call, our [agency rescue case study](/en/case-study-astro-migration-agency-rescue-2026/) documents a full stack migration where load time went from 12 s to 0.3 s after a bad agency delivery. And if you want to keep using AI in the build, but safely, with a human gate, that is exactly what our [AI implementation for companies](/en/services/ai-implementation/) covers: agents and tooling with version control, tests, and review built in. ## What you get A working, accountable site: insecure generated code rewritten, broken flows repaired, AI-slop content cleaned, performance restored, and a short guardrails document so the next round of AI assistance does not reopen the same holes. Every change is reviewed by a senior engineer, not applied autonomously. ## Related services - [Auditing AI-generated WordPress plugin code](/en/auditing-ai-generated-wordpress-plugin-code/), Diagnostic patterns for vibe-coded PHP before you ship - [WordPress security audit](/en/wordpress-security-audit/), Deep security pass for high-risk generated code - [WordPress repair and technical support](/en/wordpress-repair-service-technical-support/), Ongoing support after the rescue - [AI implementation for companies](/en/services/ai-implementation/), Using AI in the build safely, with human gates - [Core Web Vitals rescue for AI-built sites](/en/core-web-vitals-rescue-ai-built-wordpress/), Staged performance recovery after AI plugin sprawl - [Core Web Vitals audit](/en/services/core-web-vitals-audit/), Formal audit with written backlog - [Agency rescue case study](/en/case-study-astro-migration-agency-rescue-2026/), Full rebuild when rescue alone is not enough - [GEO and LLMO optimisation](/en/geo-llmo-optimization/), Turning cleaned content into citations Pricing is individual and scoped after the audit. [Contact us](/en/contact/) with the site and a short note on how it was built. --- ## AMP for WordPress, speed and Core Web Vitals URL: https://wppoland.com/en/amp-accelerated-mobile-pages-en/ Description: Make your WordPress site fast with AMP. Accelerated Mobile Pages implementation for better Core Web Vitals, mobile speed optimization, and improved user experience. Published: Sun Jan 01 Updated: Sat Jan 31 Type: guide Level: advanced
## What is AMP service? AMP (Accelerated Mobile Pages) implementation service makes your WordPress site load instantly on mobile devices. With scope agreed individually, we deliver: - **AMP Implementation** - Official AMP plugin setup with three modes (Standard, Transitional, Reader) - **Core Web Vitals Optimization** - Achieve sub-1-second LCP, optimal INP, and zero CLS - **Schema.org Markup** - Structured data for Top Stories carousel and rich results - **Analytics Integration** - Track performance with amp-analytics - **Testing & Validation** - Full validation with Google Search Console
## Who needs AMP? ### News publishers Top Stories no longer requires AMP, so the question is whether the AMP Cache still buys you anything on top of a fast site. For a newsroom publishing dozens of articles a day it sometimes doess. ### E-commerce sites Reduce mobile bounce rates with near-instant page loads. AMP-compatible WooCommerce integration available. ### Businesses in emerging markets Critical for regions with poor internet infrastructure. AMP Cache serves content from Google's global CDN. ### Sites with limited dev resources Get enterprise-level mobile performance without complex custom development.
## Where does AMP work? AMP optimization benefits your site across multiple touchpoints: - **Google Search** - Top Stories carousel, rich results, AMP badges - **Mobile Devices** - Near-instant loading on all mobile browsers - **Slow Connections** - Optimized delivery via AMP Cache - **Social Media** - Faster sharing and preview experiences - **Email** - AMP for Email interactive experiences
## AMP service pricing Our AMP implementation service includes, with scope agreed individually: ### Basic package (individual quote) - Official AMP plugin installation and configuration - Standard mode or Transitional mode setup - Basic theme compatibility adjustments - Schema.org markup implementation - Google Search Console validation - Core Web Vitals baseline report ### Advanced package (individual quote) - Everything in Basic, plus: - Custom AMP template development - WooCommerce AMP integration - Advanced analytics setup - AdSense/ad integration - Page builder compatibility - Performance monitoring dashboard ### Enterprise package (individual quote) - Everything in Advanced, plus: - Multi-site AMP implementation - Custom component development - A/B testing setup - Monthly performance reports - Priority support for 6 months ### Factors affecting price - Theme complexity and customizations - Number of custom post types - WooCommerce product catalog size - Third-party integrations required - Multi-language requirements [Get a custom quote](/en/contact/) for your WordPress site.
## What are AMP (Accelerated Mobile Pages)? AMP (Accelerated Mobile Pages) is an open-source HTML framework developed by Google and the AMP Open Source Project to create fast-loading web pages for mobile devices. Launched in 2015, AMP was designed to address the growing need for faster mobile web experiences, especially in regions with limited internet connectivity. The core principle behind AMP is simple: strip down web pages to their essential content and eliminate elements that slow down loading times. This is achieved through a combination of simplified HTML, restricted JavaScript usage, and the AMP Cache system that serves content from Google's servers. ## Is AMP still worth using in 2026? A reality check before we go further: AMP is not a 2026 best practice. It is a 2015-era framework that Google demoted in 2021 when the Top Stories carousel stopped requiring it. Mobile-first indexing did not save AMP, because AMP was never the only way to be fast on mobile, and Core Web Vitals replaced it as the metric Google actually cares about. What AMP does well, technically, has not changed. Strict CSS limits, no arbitrary JavaScript, and prefetching from Google's CDN produce sub-1-second LCP almost by default, low CLS because layout rules are enforced, and predictable INP because there is no third-party script chaos. If you are working with a small team and need a mobile variant that is hard to make slow, AMP still delivers on that narrow promise. What AMP does not do anymore is buy you preferential search treatment. The AMP badge is gone in most markets. Top Stories ranks canonical URLs with good Core Web Vitals just as readily as AMP URLs. The crawl-efficiency argument matters only at very large scale. So if you are considering AMP for the SEO upside specifically, that upside is mostly historical at this point. ## How AMP works ### AMP HTML AMP uses a specialized version of HTML with custom tags and restrictions: - All CSS must be inlined and limited to 75KB - JavaScript is restricted to AMP-approved components - Images and videos use lazy loading by default - Third-party scripts are sandboxed to prevent blocking ### AMP cache Google's AMP Cache stores AMP pages and serves them directly from Google's infrastructure, providing: - Pre-validation of AMP pages - Compression and optimization - Global CDN distribution - Near-instantaneous delivery ### AMP JavaScript The AMP JS library implements best performance practices: - Asynchronous loading of all resources - Pre-calculation of layout before loading - Sandboxed iframes for third-party content - Built-in analytics support ## Which WordPress AMP plugins are still maintained in 2026? Before touching anything, look at what is actually still maintained. The official **AMP** plugin (`amp` on WordPress.org, formerly "AMP for WordPress") is the only one I would still install on a new project. It is the plugin Google and Automattic backed, and it still ships updates against current WordPress and PHP versions. Three modes: Standard (the whole site is AMP), Transitional (AMP and canonical coexist), Reader (legacy templates, mostly relevant for old blogs you do not want to rebuild). Standard mode is the cleanest, but it will surface every theme incompatibility you have. Most paid themes from 2018-2020 break Standard mode in ways that take a day to fix. **AMP for WP, Accelerated Mobile Pages** by Ahmed Kaludi and Mohammed Kaludi is the other plugin most clients arrive with. It is more permissive about ads, page builders, and WooCommerce, which is why agencies pushed it. Update cadence has slowed, GitHub issues sit open for months, and the WooCommerce add-ons broke in several WC 8.x releases. Functional, but I now treat it as legacy code I am migrating off, not a fresh choice. **Penci AMP**, **Schema AMP** and the long tail of theme-bundled AMP plugins are mostly abandoned. If a client is on one of these, the migration question is not "if" but "when". Whichever plugin you are on, the setup that actually matters is small: pick Standard or Transitional, run the AMP validator from Search Console (while it still exists), wire `amp-analytics` to your existing GA4 property, and audit which canonical-page features silently disappear on the AMP variant. The plugin's UI will not warn you when it strips your contact form's reCAPTCHA or your booking widget's JavaScript. ## Common AMP problems on WordPress sites The AMP failure modes I keep seeing on real client sites, not the theoretical ones from the docs: **Validation errors after a plugin update.** A perfectly valid AMP variant breaks the morning after someone activates a popup plugin, a chat widget, or a new analytics snippet. The non-AMP analytics tag injects inline JavaScript, and AMP rejects the whole page. The fix is removing the offending plugin's frontend output from the AMP variant via the official plugin's "Plugin Suppression" panel, but every new plugin reopens the question. **The 75 KB inline CSS limit.** AMP requires all CSS inlined into a single ` ' . esc_html__( 'Staff accounts only. Report a suspected password leak to IT.', 'your-textdomain' ) . '

'; return $notice . $message; } add_filter( 'login_message', 'wppoland_login_message' ); ``` Implementation notes: - Escape the text (`esc_html__`), or build the markup through `wp_kses_post` if it needs a link. - Do not replace `$message` blindly. Core puts its own notices there (after `loggedout=true`, for example). Prepend, or filter conditionally on the request. - The `.message` class is already styled by `login.css`. Your `style-login.css` should respect it (text colour, left border), or the banner disappears into the background. For text that depends on the action (`login`, `lostpassword`, `rp`), check `$_GET['action']` instead of showing the same note on password recovery and on the 2FA step. ## Add custom CSS to the WordPress login page Do not dump login rules into the public `style.css`. Enqueue a dedicated file on the same hook: ```php function wppoland_login_stylesheet() { wp_enqueue_style( 'wppoland-custom-login', get_stylesheet_directory_uri() . '/style-login.css', array(), wp_get_theme()->get( 'Version' ) ); } add_action( 'login_enqueue_scripts', 'wppoland_login_stylesheet' ); ``` Example `style-login.css`: ```css body.login { background-color: #0d1117; display: flex; align-items: center; justify-content: center; } .login form { background: #161b22; border: 1px solid #30363d; box-shadow: none; border-radius: 8px; } .login label { color: #c9d1d9; } .wp-core-ui .button-primary { background: #238636; border-color: rgba(27, 31, 35, 0.15); } .login #backtoblog a, .login #nav a { color: #c9d1d9; } ``` Match contrast to WCAG where editors need it: pale grey on dark grey fails for some users. Test focus outlines on the username, password, and submit controls; removing outlines for "clean design" is a regress. Language packs and RTL locales still use the same hooks. Avoid hard-coded English strings in CSS `content` properties. Background images on `body.login` should be compressed and sized for a simple full-bleed photo, not a 4K marketing hero. Login traffic is frequent for editors; a heavy background taxes every session. Prefer a solid brand colour or a subtle pattern under 100 KB. If the brand book mandates a light form on a light background, increase border contrast on `.login form` so the card still reads as a distinct surface. Many "minimal" logins fail because the form melts into the page. ## Change the WordPress login error message A failed password for an existing user historically produced a message that confirmed the username. That helps attackers enumerate accounts before brute-forcing passwords. ```php function wppoland_login_errors() { return __( 'Invalid credentials.', 'your-textdomain' ); } add_filter( 'login_errors', 'wppoland_login_errors' ); ``` Keep the copy boring and identical for unknown user and wrong password paths. This is obscurity for enumeration, not a substitute for: 1. Disallowing weak admin passwords. 2. Limiting login attempts at the application or edge. 3. Turning on 2FA for administrators. 4. Not using `admin` as a username on new installs. Lost-password flows have their own messages. Review those templates if you customise registration or membership plugins that share the login CSS. Some membership plugins replace `wp-login.php` with a front-end form. In that case these hooks never fire. Apply the same rules on the plugin's form hooks, or style their template partials directly. Confirm which URL editors actually use before you declare the job done. XML-RPC and the REST users endpoint are separate attack surfaces. Branding `wp-login.php` does nothing for them. Disable or [restrict XML-RPC](/en/wordpress-security-hardening-complete-guide-2026/) if you do not need it, and keep application passwords under policy if the site uses them. ## Disable the WordPress login shake animation Failed logins trigger a shake via `wp_shake_js`. Some brand guidelines treat it as noise. The snippet most tutorials still copy, `remove_action( 'login_head', 'wp_shake_js', 12 )`, has done nothing since WordPress 5.4. Core now attaches `wp_shake_js` to `login_footer`, and only conditionally, inside `login_header()`. You can see it in [`wp-login.php` in the wordpress-develop repository](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-login.php); the 5.3 branch still used `login_head`. The stable method is the [`shake_error_codes`](https://developer.wordpress.org/reference/hooks/shake_error_codes/) filter, available since 3.0: an empty list of error codes means no animation. ```php add_filter( 'shake_error_codes', '__return_empty_array' ); ``` The filter is public API, so it does not depend on the priority or on which hook core happens to print the script from. Add a one-line comment above it that states the intent. ## Style 2FA and passkey fields on wp-login.php Building full 2FA inside `functions.php` is the wrong project. Use a maintained plugin (for example the official Two Factor plugin or your host's offering) and style the extra fields so they match the branded form. The [Two Factor](https://wordpress.org/plugins/two-factor/) plugin from the WordPress.org directory (version 0.17.0, 100,000+ active installs according to the directory API in October 2026) emits these classes on the second login step. They are taken from the 0.17.0 source: - `.two-factor-prompt` for the instruction paragraph, - `input.authcode` (with `id="authcode"`) for the TOTP code, the email code and the backup code; the field carries `inputmode="numeric"` and `autocomplete="one-time-code"`, so do not override those attributes with a script, or phones stop offering the code from SMS or the password manager, - `.backup-methods-wrap` for the links to alternative methods, - `.two-factor-email-resend` for the button that resends the email code. ```css .login .two-factor-prompt { color: #c9d1d9; } .login input.authcode { letter-spacing: 0.2em; text-align: center; } .login .backup-methods-wrap a { color: #c9d1d9; } ``` Older tutorials use selectors such as `.backup-methods` or `.two-factor-methods`. The current plugin code has neither, so those rules never match. Load the working rules in the same `style-login.css`. After every plugin update, re-check the second login step on staging, because class names drift. Other 2FA and passkey plugins have their own markup: read their selectors from their code, not from this guide. …[truncated, fetch the canonical URL for the full body]… --- ## Googlebot and JSON-LD: a single unescape pass URL: https://wppoland.com/en/googlebot-json-ld-single-unescape-pass-2026/ Description: Google changed its JSON-LD extraction and now applies only one pass of HTML unescaping. Double-escaped entities are no longer unrolled, so the block stops parsing and the structured data disappears. How to measure your own corpus and how to encode it correctly. Published: Sun Aug 30 Updated: Wed Oct 14 Type: guide Level: intermediate Tags: json-ld, structured-data, googlebot, schema-org, technical-seo
Google has stopped repairing your structured data. JSON-LD extraction now applies a single pass of HTML unescaping, so a block that used to be quietly straightened out simply fails to parse and disappears. There is no error in Search Console, no warning, only a rich result that stops showing up. This piece shows how to measure your own corpus in a few minutes instead of guessing, where that encoding comes from in WordPress, and why a one-off audit is not enough. Our own measurement across 68 055 blocks is in here, along with the script.
## What changed in Google's JSON-LD extraction in 2026 The statement is short and worth quoting whole, because everything else follows from one sentence: > To bring our parser up to JSON and other standards, we changed our JSON-LD extraction and are now only applying a single pass of HTML unescaping Gary Illyes added a pointer to where correctness is defined: RFC 8259, the JSON specification. That is not an SEO guideline, it is a reference to the standard every JSON parser in the world already follows. The practical consequence is stated just as briefly: double-escaped entities, such as `&` or `✔`, will no longer be unrolled. Google's parser used to make an extra pass and straighten that out. It now makes one pass and is left with text that is not valid JSON. It is worth naming what the announcement does not contain. There is no rollout date. There is no link to updated Google documentation. The source is a LinkedIn post, reported by Search Engine Roundtable on 21 August 2026. Treat it as a state Google described, not as a specification you can cite back to a client. ## What is double escaping in JSON-LD Take a simple case: the company name "Smith & Sons" in a JSON-LD `name` field. | written as | what the JSON parser sees | status | | --- | --- | --- | | `"Smith & Sons"` | Smith & Sons | valid, JSON does not require escaping an ampersand | | `"Smith \u0026 Sons"` | Smith & Sons | valid, universal escape | | `"Smith & Sons"` | Smith & Sons | parses, but the value is wrong | | `"Smith & Sons"` | Smith & Sons | this is double escaping | An ampersand on its own will not blow up the block, because JSON does not require it to be escaped. The real trouble starts with the quotation mark. If a template writes `"` where `\"` belongs, one unescape pass leaves you with `"` rather than a quote character. The string never closes and the block stops being JSON. Where does it come from in WordPress? Almost always from processing the same value twice. Content passes through `esc_html()` on save, then through a theme filter on output, and finally lands in a JSON-LD field that would have escaped it properly on its own. Each of those steps is correct in isolation. Composed together they produce an encoding that worked for years only because Google was straightening it out for you. ## How to check JSON-LD for double escaping The rule that matters most: check the built HTML, not the template source. The source always looks fine, because the escaping is added on output. If you ship a static build, scan the output directory. On a classic WordPress install, pull a sample of URLs with `wget` or `curl` and scan what the server actually returned. The check itself is a dozen lines. Pull out every `'; }); ``` ## 3. How to lazy load YouTube videos in WordPress Video embeds are traditionally heavy and can ruin your LCP (Largest Contentful Paint) scores. A single YouTube embed can add 2MB+ to your page weight and significantly delay interactivity. ### How the YouTube facade pattern works This applies to article and product pages where the video supports the text. A dedicated watch page is the exception, explained in the facade section further down. Never load the video player on initial page load. Instead, use a **Thumbnail Placeholder** that only loads the actual player when the user interacts with it. **How It Works:** 1. Display a high-resolution thumbnail image with a play button overlay 2. Load the heavy JavaScript only when the user clicks or scrolls the video into view 3. Replace the thumbnail with the actual video player on demand **Implementation Example:** ```html
Video: WordPress SEO Tutorial
``` ```javascript // Lazy load video on interaction document.querySelectorAll('.video-facade').forEach(facade => { facade.addEventListener('click', () => { const videoId = facade.dataset.videoId; facade.innerHTML = ` `; }); }); ``` ### How lazy loading video affects Core Web Vitals | Metric | Direct Embed | Lazy-Loaded | |--------|--------------|-------------| | LCP | 3.2s | 1.1s | | TTI | 4.8s | 1.9s | | Page Weight | +2.1MB | +45KB | | Mobile Score | 42/100 | 94/100 | ## 4. How video supports E-E-A-T and conversions In 2026, video isn't just for entertainment - it's a critical component of [E-E-A-T](/en/wordpress-seo-2026-eeat-ai-search-guide/) (Experience, Expertise, Authoritativeness, Trustworthiness) that Google uses to evaluate content quality. ### How video builds trust **Expert Interviews**: Featuring recognized experts in your videos builds credibility that text alone cannot achieve. Seeing a real person speak creates a connection that establishes trust. **Product Demonstrations**: On a [WooCommerce site](/en/woocommerce-developer/), a demo video shows the product working. Marketing surveys quote high conversion lifts for that format; the original studies are vendor surveys, not Google measurements, so we do not repeat a percentage. Show the product in action anyway. **Behind-the-Scenes Content**: Authentic glimpses into your process humanize your brand and differentiate you from AI-generated content. ### Which video types convert best 1. **Tutorial Videos**: How-to content that solves specific problems 2. **Testimonials**: Customer success stories in video format 3. **Product Comparisons**: Side-by-side feature demonstrations 4. **FAQ Videos**: Address common objections and questions 5. **Webinar Recordings**: Long-form educational content ### How to show E-E-A-T in a video - Show the speaker/expert clearly in the video - Include credentials and experience in the video description - Link to author profiles and social media - Use professional production quality (good lighting, clear audio) - Include publication dates to show content freshness ## 5. YouTube, TikTok and LinkedIn video SEO ### YouTube SEO in 2026 YouTube's algorithm has evolved to prioritize watch time and engagement over simple view counts: **Title Optimization:** - Front-load primary keywords - Use numbers and brackets for CTR boost: "[2026 Guide]" - Keep under 60 characters for full display - Example: "WordPress SEO: 10 Tips for 2026 [Tutorial]" **Description Best Practices:** - First 100 characters are crucial (appear in search) - Include timestamps for longer videos - Add relevant links with UTM parameters - Use 3-5 relevant hashtags **Thumbnail Strategy:** - 1280x720 resolution minimum - Readable text at small sizes (under 20 characters) - High contrast colors - Consistent branding across your channel - Include human faces when possible (increases CTR) ### Does TikTok video help SEO Short-form video (under 60 seconds) has exploded in 2026. While TikTok doesn't directly impact Google rankings, it drives significant brand awareness and traffic: - Repurpose long-form content into 30-60 second clips - Use trending sounds and hashtags - Post consistently (1-3 times daily) - Include clear CTAs linking to your WordPress site - Embed TikTok videos on your site for cross-promotion ### How to use LinkedIn video for B2B For WordPress agencies and B2B service providers: - Native LinkedIn video usually holds attention better than a bare link; measure yours, do not copy an infographic multiple - Keep videos under 2 minutes for maximum reach - Use professional captions (many watch without sound) - Share case studies and client success stories ## 6. Video vs text content for SEO | Metric | Text-only article | Article + video | Video-first page | |--------|-------------------|-----------------|------------------| | Time on page | Baseline | Wistia measured about 1.4x vs pages without video | Measure on your own property | | Bounce and conversion | Do not copy a template rate | Do not copy a template rate | Read Search Console and shop analytics | | Search visibility | Web results | Web + video features | Web + video + YouTube | | What a shopper can see | Specs and photos | Specs plus a working demo | Demo first, text as support | ## 7. Best WordPress video plugins for SEO ### Presto Player, Videopack and WP Rocket for video …[truncated, fetch the canonical URL for the full body]… --- ## How to downgrade WordPress? (Plugin, FTP, wp-CLI) URL: https://wppoland.com/en/how-to-downgrade-wordpress-rollback-guide/ Description: Update crashed your site? Don't panic. See 3 proven ways to rollback WordPress core, plugins, or themes to a previous version. Published: Tue Jan 03 Updated: Wed Oct 14 Type: guide Level: advanced Tags: update, backup, wp-cli, debugging You click "Update", wait a moment, and suddenly see the "White Screen of Death" or a critical PHP error. Your site is down, and the client is calling. Don't panic. In WordPress, you can almost always revert changes. In this guide, I'll show you three methods for a **downgrade**, ranging from easiest to most advanced. Before that, one step saves you from fixing the wrong thing: figure out what actually broke, so you roll back the specific component at fault instead of guessing. --- ## How to find which update broke your WordPress site A rollback is only useful if you revert the right thing. If a theme update broke the layout and you downgrade WordPress core, you waste time and add risk. Spend five minutes on diagnosis first. ### How to enable WP_DEBUG and read debug.log WordPress hides fatal errors behind the white screen by default. Switch on debugging so the real message is written to a file rather than shown to visitors. Edit `wp-config.php`, above the line that says `/* That's all, stop editing! */`: ```php define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); define( 'WP_DEBUG_DISPLAY', false ); ``` With `WP_DEBUG_LOG` on, errors land in `wp-content/debug.log`. Open that file and read the last entries. A typical line names the exact file and function, for example a fatal error in `wp-content/plugins/some-plugin/includes/class-loader.php`. That path tells you which plugin is at fault before you touch anything. If `debug.log` is empty or missing, check your host's own logs. Most panels expose a PHP error log and a server (Apache or Nginx) error log under a "Logs" or "Metrics" section. The server log catches memory-limit and timeout failures that never reach the WordPress log. ### How to find the faulty plugin by deactivating plugins If the log points at a plugin, or if nothing is conclusive, deactivate plugins to narrow it down. With wp-admin still reachable, deactivate them one at a time and reload the broken page after each. When the error clears, the last plugin you switched off is the cause. If you are locked out of the dashboard, do the same over FTP or SSH. Renaming the whole `wp-content/plugins` folder to `plugins_off` deactivates every plugin at once. If the site returns, rename it back and disable plugins individually until the offender shows itself. Over WP-CLI the same check is two commands: `wp plugin deactivate --all`, then reactivate one by one. ### Should you downgrade core, a plugin, a theme or PHP Match the symptom to the layer, because each one rolls back differently: - **A plugin** is the most common cause. Downgrade that single plugin (Method 1 or 2 below) and leave everything else alone. - **A theme** update usually breaks layout, styling, or the block editor rather than throwing a fatal error. Roll the theme back, or switch to a default theme like Twenty Twenty-Four to confirm. - **WordPress core** is the culprit when the site broke immediately after a core update and plugins are ruled out. Core needs Method 2 or 3. - **A PHP version bump** breaks the site when your host upgraded PHP (often automatically), not WordPress. The tell is a fatal error mentioning a deprecated or removed function even though you did not update anything in WordPress. That is a hosting-panel fix, covered further down. Only once you know which layer failed does picking a rollback method make sense. --- ## What to check before you downgrade WordPress A rollback destroys evidence. Before you replace a single file, record what the site is running right now, so you can move forward again once the bug is patched and so you can compare states if the rollback does not help. With WP-CLI it takes under a minute: ```bash wp core version --extra wp plugin list --fields=name,status,version,update_version --format=csv > plugins-before.csv wp theme list --fields=name,status,version --format=csv > themes-before.csv wp db export backup-before-rollback.sql ``` `wp core version --extra` prints the core version together with the database revision that the running code expects. Compare it with `wp option get db_version`, the revision actually stored in the database: if the stored number is higher than the one your target release expects, the database has already migrated forward and a file-only rollback will not match it. The two CSV files give you the exact versions to return to afterwards, instead of guessing from memory at midnight. Two plugin headers decide whether a core downgrade is even possible. A plugin can declare `Requires at least` and `Requires PHP` in its main file header, and where those headers are present WordPress blocks activation when the running core or PHP version does not meet them. So if a plugin declares WordPress 6.6 and you drop core to 6.4, you cannot reactivate it after the downgrade, and its code may call core functions that the older release does not ship. Read the headers before you pick a target version: ```bash grep -RE "Requires (at least|PHP)" wp-content/plugins/*/*.php ``` Finally, put the site behind maintenance mode so visitors and crawlers get a 503 instead of a half-replaced install: `wp maintenance-mode activate`, and `wp maintenance-mode deactivate` when the work is done. A 503 with a short retry window is the response Google expects during maintenance, so it does not treat the outage as a permanent change. --- ## Method 1: revert a plugin update with WP Rollback If you still have access to the Dashboard (wp-admin), you are saved. The best tool for reverting plugins and themes is the free **WP Rollback** plugin. 1. Install and activate **WP Rollback**. 2. Go to **Plugins > Installed Plugins**. 3. A new link **"Rollback"** will appear next to each plugin. 4. Click it and choose the version you want to return to (e.g., from 5.4 to 5.3). 5. Confirm. Done! **Note:** The free version of WP Rollback works with plugins and themes from WordPress.org, according to its [plugin page](https://wordpress.org/plugins/wp-rollback/); premium plugins need the paid Pro version. It does not revert WordPress core. For core, use a separate plugin, **Core Rollback** by Andy Fragen, which sits under **Tools**. --- ## Method 2: downgrade the WordPress version with WP-CLI If you have SSH access (see our [SSH guide](/en/ssh-for-wordpress-developers-guide/)), this is the fastest and safest method. To downgrade WordPress Core: ```bash wp core update --version=7.0.7 --force ``` The `--force` parameter, in the words of the [WP-CLI docs](https://developer.wordpress.org/cli/commands/core/update/), updates "even when installed WP version is greater than the requested version". To downgrade a plugin (e.g., WooCommerce): ```bash wp plugin install woocommerce --version=8.5.0 --force ``` Here `--force` belongs to `wp plugin install`, where it overwrites any installed version without prompting ([docs](https://developer.wordpress.org/cli/commands/plugin/install/)). `wp plugin update` has no `--force` flag, although it also accepts `--version`. Themes follow the same pattern: `wp theme install twentytwentyfour --version=1.0 --force`. ### Which WordPress version to roll back to Not every older release is a safe target. WordPress.org publishes the security status of every core release through the [stable-check API](https://api.wordpress.org/core/stable-check/1.0/), and the Core Rollback plugin uses the same list: its [description](https://wordpress.org/plugins/core-rollback/) says it only offers the latest release and outdated, secure ones. On 14 October 2026 the API marks 7.1.3 as `latest`, the final point release of each older branch (7.0.7, 6.9.10, 6.8.11 and so on) as `outdated`, and every earlier point release of those branches as `insecure`. The 6.4.3 this guide used as its example until this update is on the insecure list. So the rule is: roll back to the last point release of the branch that worked, not to the exact number you ran before. If 7.1.x broke the site, the target is 7.0.7, not 7.0.2. Check the status before you run the command: ```bash curl -s https://api.wordpress.org/core/stable-check/1.0/ | grep '"7.0.7"' ``` The second check is PHP. The core team's [PHP compatibility table](https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/) lists which PHP versions each release is tested with, and a rollback can move you off it: | Server PHP | Oldest core release the table lists for it | | --- | --- | | 8.5 | 6.9 | | 8.4 | 6.7 | | 8.3 | 6.4 | | 8.2 | 6.1 | If the host has already moved the server to PHP 8.5, rolling back to 6.8 lands on a combination the core team never listed as supported. The limit runs the other way too: WordPress 7.0 dropped support for PHP 7.2 and 7.3, so on such a server 6.9 is the newest release you can run. Pick the target that passes both checks: secure according to stable-check and listed for the PHP version your server runs. `wp eval 'echo PHP_VERSION;'` shows the version WordPress actually sees. --- ## Method 3: downgrade WordPress manually via FTP If you have no access to the panel or SSH, only "open heart surgery" via FTP remains. **This is risky. Backup your database before starting!** 1. Go to [WordPress.org Releases](https://wordpress.org/download/releases/) and download the older WP version (zip). If you have WP-CLI on your own computer, `wp core download --version=7.0.7 --skip-content --path=wp-7.0.7` fetches the same release without the default themes and plugins ([docs](https://developer.wordpress.org/cli/commands/core/download/)). 2. Unzip it on your computer. 3. **DELETE** the `wp-content` folder and `wp-config-sample.php` file from the unzipped folder. * *Why?* So you don't accidentally overwrite your photos, themes, and configuration! 4. Connect via FTP (FileZilla). 5. Upload the remaining files (`wp-admin`, `wp-includes`, root files) to the server, choosing **"Overwrite"**. After uploading, try accessing `wp-admin`. If WordPress shows **Database Update Required**, confirm with **Update WordPress Database**. --- ## How to restore WordPress from a hosting backup Before you touch a single file, check whether your host can undo the update for you. On managed WordPress hosting this is often the fastest route, and it needs no command line at all. Most managed hosts keep automatic daily restore points and expose a one-click restore in the control panel, usually under a "Backups" or "Restore" tab. You pick a snapshot from before the update and confirm. The host rebuilds the site (files and database together) to that moment. Because both layers move back in sync, this sidesteps the schema-mismatch trap described in the next section. Two things to check first. Some hosts restore files and database separately, so read the labels and choose the full snapshot, not files-only. And a restore overwrites everything since the snapshot, so any orders, comments, or posts created after that point are lost. On a busy store, weigh that against a targeted plugin rollback. For a brochure site that changed nothing overnight, the one-click restore is usually the cleanest option. --- ## How to tell whether the rollback actually worked "The homepage loads" is not verification. A rollback can leave a half-copied file tree, a stale bytecode cache, or a broken admin path while the front page still renders from cache. Start by checking the version that is actually in place, not the one you intended to install: ```bash wp core version wp plugin get woocommerce --field=version wp core verify-checksums wp plugin verify-checksums --all ``` …[truncated, fetch the canonical URL for the full body]… --- ## Core Web Vitals 2026: The Complete INP Optimization Guide for WordPress URL: https://wppoland.com/en/core-web-vitals-2026-inp-optimization-guide/ Description: How to optimize Interaction to Next Paint (INP) on WordPress sites. Practical fixes for the newest Core Web Vital metric that directly impacts Google rankings. Published: Sat Mar 21 Updated: Wed Oct 14 Type: guide Level: advanced Tags: core-web-vitals, inp, interaction-to-next-paint, page-speed, wordpress-performance, lcp, cls
Google's Core Web Vitals are no longer optional for SEO. In 2026, these metrics directly influence search rankings, and **INP (Interaction to Next Paint)** has become the most challenging one for WordPress sites. While LCP and CLS can be fixed with image optimization and layout reservations, INP requires fundamentally rethinking how JavaScript runs on your pages. INP replaced FID (First Input Delay) as a Core Web Vital in March 2024. The difference is significant: FID only measured the first interaction, while INP measures **every interaction** throughout the entire page visit. A site could score well on FID but terribly on INP - and many WordPress sites do.
## Core Web Vitals thresholds for LCP, INP and CLS ### Good and poor values for LCP, INP and CLS | Metric | What It Measures | Good | Needs Work | Poor | |---|---|---|---|---| | **LCP** (Largest Contentful Paint) | Loading speed | < 2.5s | 2.5-4s | > 4s | | **INP** (Interaction to Next Paint) | Responsiveness | < 200ms | 200-500ms | > 500ms | | **CLS** (Cumulative Layout Shift) | Visual stability | < 0.1 | 0.1-0.25 | > 0.25 | ### Why is INP harder to fix than LCP and CLS LCP is primarily about server speed and [image optimization](/en/wordpress-image-optimisation-critical-css-performance-guide/) - well-understood problems with clear solutions. CLS is about reserving space for dynamic content - a CSS and HTML discipline. INP is about **JavaScript execution efficiency** - a fundamentally harder problem because it requires understanding the browser's main thread, task scheduling, and event handling. WordPress sites are particularly vulnerable to poor INP because: 1. **Plugin bloat** - every plugin can add JavaScript that blocks the main thread 2. **Page builders** - Elementor, Divi, WPBakery add heavy frontend frameworks 3. **Third-party scripts** - analytics, ads, chat widgets, social embeds all compete for main thread time 4. **jQuery dependency** - many WordPress themes and plugins still rely on jQuery, adding 85KB+ of blocking JavaScript 5. **No code splitting** - traditional WordPress themes load all JavaScript on every page ## How INP works ### What are the three phases of an interaction When a user clicks a button, taps a link, or presses a key, the browser processes it in three phases: 1. **Input delay** - time between the physical interaction and the browser starting to process the event handler (caused by main thread being busy with other tasks) 2. **Processing time** - time to execute all event handlers for this interaction 3. **Presentation delay** - time to render the visual update after event handlers complete INP = Input delay + Processing time + Presentation delay The metric reports the **slowest interaction** of the visit. On busy pages the rule is slightly softer: according to [web.dev](https://web.dev/articles/inp), one highest interaction is ignored for every 50 interactions. Most pages see fewer than 50 interactions per visit, so in practice the worst one is what gets reported. Google then rates the page at the 75th percentile of all visits recorded in the field. This means a single slow interaction can tank your INP score, even if every other interaction is fast. On a typical WordPress site with forms, menus, filters and carousels there are dozens of possible interactions, and one bad one is enough. INP became a Core Web Vital and replaced FID on 12 March 2024, as announced on [web.dev](https://web.dev/blog/inp-cwv-march-12). FID was deprecated in the same transition and removed from the Search Console report. ### What counts as an interaction Not every user action registers as an interaction for INP purposes. The distinction matters because optimizing for the wrong events wastes effort. **These actions DO trigger INP:** - **Clicks** on buttons, links, toggles, menus, checkboxes, radio buttons - **Taps** on mobile (touch events including `pointerdown`, `pointerup`, `click`) - **Key presses** in input fields, search boxes, text areas, and contenteditable elements - **Keyboard shortcuts** - pressing Enter to submit a form, Space to toggle a checkbox - **Custom interactive elements** - any element with a `click`, `pointerdown`, or `keydown` handler attached **These actions do NOT trigger INP:** - **Scrolling** - scroll is measured separately and does not count as a discrete interaction - **Hovering** - mouse hover alone doesn't generate an INP entry, even if a tooltip appears - **Pinch-to-zoom** - this is a continuous gesture, not a discrete interaction - **CSS-only transitions** - animations triggered purely by CSS (`:hover`, `:focus`) without JavaScript event handlers - **Page load** - the initial rendering of the page is not an interaction; that's what LCP measures One nuance: a single physical action like a tap can generate multiple events (`pointerdown`, then `mousedown`, `pointerup`, `mouseup` and finally `click`). The browser groups these into a **single interaction** and measures the total duration from the first event to the final paint. This grouping means you don't get penalized five times for one tap, but it also means slow handlers on any of those grouped events will increase the overall interaction duration. ## Measuring INP on WordPress sites ### Google Search Console (field data) The most important data source. Navigate to Core Web Vitals, then Mobile or Desktop. Search Console shows real-user INP data aggregated over 28 days. Pages are grouped by URL pattern and rated as Good, Needs Improvement, or Poor. Always start here. Field data reflects what real users experience, including slow devices, weak connections and interactions that lab tests never simulate. If Search Console reports poor INP, Google is seeing that problem, and it can affect rankings. ### PageSpeed Insights (lab + field) Enter any URL and get both: - **Field data** - real user measurements from the Chrome UX Report (CrUX). This is the data Google uses for its assessment - **Lab data** - simulated measurements from Lighthouse, run in a controlled environment For INP, field data matters more because lab tests may not trigger the same interactions real users perform. A site can show a clean lab report and still have poor field INP, because real users open complex menus, search filters and forms that Lighthouse never touches. ### Chrome DevTools (debugging) To diagnose a specific INP problem, Chrome DevTools is the tool. The process: 1. Open DevTools, go to the **Performance** panel and click **Record** 2. Interact with the page: click buttons, open menus, fill in forms 3. Stop the recording and analyse the result Look for: - **Long tasks** - tasks over 50ms, marked with a red triangle in the corner. Any task above this threshold blocks the main thread - **Event handlers** - how long each click or tap takes to process. If one click triggers several handlers, the time adds up - **Layout thrashing** - forced synchronous layouts during interactions. This happens when JavaScript alternately reads and writes layout properties, forcing the browser to recalculate again and again ### How to read a waterfall chart in DevTools The Performance panel waterfall can be overwhelming, but a systematic approach makes it manageable. After recording an interaction, focus on these areas: 1. **Find the interaction marker** - Chrome marks discrete interactions with a dashed vertical line in the timeline. Click it to highlight the associated events. 2. **Check the main thread flame chart** - look directly below the interaction marker for the stack of function calls. The widest bars represent the longest-running functions - these are your optimization targets. 3. **Identify the three INP phases** - input delay appears as idle time before the first event handler fires, processing time spans the event handler execution, and presentation delay covers style recalculation, layout, and paint after handlers complete. 4. **Look for forced reflows** - purple "Layout" bars inside event handlers indicate synchronous layout calculations. These appear when JavaScript reads a layout property (like `offsetHeight`) after modifying the DOM, forcing the browser to recalculate layout immediately rather than batching it. 5. **Check the "Interactions" track** - Chrome DevTools has a dedicated Interactions lane that shows each interaction's total duration and its rating (green/yellow/red). This gives you a quick pass/fail before diving into the flame chart. A common pattern on WordPress sites: clicking a menu toggle triggers a 200ms long task because the event handler forces a reflow by reading `element.getBoundingClientRect()` immediately after adding a CSS class. Moving the DOM read before the DOM write eliminates the forced reflow and can cut interaction time by 60-80%. ## How to improve INP in WordPress ### 1. Defer non-critical JavaScript The single most impactful fix. Most WordPress plugins load JavaScript in the ``, blocking the main thread before the page even renders: ```html ``` The `defer` attribute tells the browser to download the file in parallel but execute it only after the HTML has been fully parsed. That frees the main thread during the initial load and cuts input delay for early interactions. Plugins that benefit most from deferring: - **Analytics** (GA4, GTM) - add `defer` or load via `requestIdleCallback` - **Chat widgets** (Intercom, Tawk.to) - load after user scroll or after 5 seconds - **Social embeds** (Facebook, Twitter) - load only when visible (Intersection Observer) - **Sliders and carousels** - defer initialization until visible ### 2. Remove unused plugin JavaScript Many plugins load their scripts on every page of the site, whether they are needed there or not. Audit which plugins actually need frontend JavaScript: ```bash # List the scripts enqueued on the front end wp eval 'do_action("wp_enqueue_scripts"); global $wp_scripts; foreach ($wp_scripts->queue as $s) echo $s . "\n";' ``` `wp eval` runs outside a page request, so the `wp_enqueue_scripts` hook has to be fired by hand, otherwise the queue is empty. Scripts that a plugin enqueues only for a specific template or query will not show up this way. For a per-page view, the [Query Monitor](https://wordpress.org/plugins/query-monitor/) plugin lists the enqueued scripts of the page you are looking at. From that list, mark the ones that page does not need. Common offenders: - **Contact Form 7** loads its CSS and JavaScript on every page, but the forms usually live on one or two pages. Fix: load it conditionally, only where a form exists - **WooCommerce cart fragments** - the cart fragments AJAX runs on every page, even outside the shop. Fix: disable it on pages that are not part of the store - **Gutenberg block styles** - WordPress loads CSS and JavaScript for all registered blocks, even ones not used on the page. Fix: strip the styles of unused blocks Conditional loading can be done in the theme's `functions.php` or with an asset manager plugin such as Asset CleanUp or Perfmatters. ### 3. Break long tasks Any JavaScript task over 50ms blocks the main thread. While it runs, the browser cannot respond to user input, which directly raises the input delay part of INP. The fix is to split long tasks into smaller pieces and hand control back to the browser between them. `scheduler.yield()` is the modern API. It is not available in every browser yet (the [web.dev long tasks guide](https://web.dev/articles/optimize-long-tasks) lists Chrome and Edge 129 and Firefox 142, with Safari still missing), so wrap it with a `setTimeout` fallback and yield on a deadline: …[truncated, fetch the canonical URL for the full body]… --- ## Corporate web development in Spain with Astro and headless WordPress in 2026 URL: https://wppoland.com/en/corporate-web-development-astro-headless-wordpress-spain/ Description: A detailed look at rolling out Astro and headless WordPress for corporate websites in Spain. Mobile performance, Core Web Vitals and SEO in 2026. Published: Fri May 08 Updated: Wed Oct 14 Tags: astro, headless, guide, 2026 # Corporate web development in Spain with Astro and headless WordPress in 2026 The Spanish corporate market in 2026 demands an unprecedented level of digital rigour. Mid-sized and large companies in Spain understand that their online presence is not a static business card but a direct commercial tool that generates business. With mobile traffic dominating browsing and Google penalising slow sites hard, the classic corporate web architecture built on monolithic CMSs has speed and security limits that are hard to justify to a board of directors. In response, the **Astro and headless WordPress** architecture has become the technical reference standard in Spain for leading brands in B2B, retail and the institutional sector. The model combines the familiarity and editorial power of WordPress with the speed and security of Astro on the frontend. This technical guide walks through the components of the architecture, integration best practices and their direct impact on SEO and GEO (Generative Engine Optimization) in the Spanish market. --- ## 1. Why use headless WordPress for a corporate website For years, IT and marketing departments in Spanish companies have been in constant conflict over the organisation's content management system (CMS): - **Marketing** insists on WordPress because its writing teams know the interface, are fluent in the Gutenberg editor, can schedule posts quickly and rely on the plugin ecosystem for content optimisation. - **IT / development** rejects WordPress because of its history of security problems (recurring plugin vulnerabilities), the complexity of maintaining Apache/Nginx servers with FPM and, above all, poor load performance caused by repeated queries to the MySQL database and the code overhead injected by classic themes. A [headless architecture](/en/nextjs-astro-migration/) settles this dilemma at the root: ```mermaid graph LR subgraph Backend Privado A[WordPress Core + Gutenberg] --> B[Base de Datos MySQL] A --> C[REST API / GraphQL] end subgraph Frontend Público C --> D[Compilación de Build en Astro] D --> E[Cloudflare Pages & Workers] E --> F[HTML estático + CSS ligero] end ``` Once the CMS is decoupled, the marketing team keeps using the usual WordPress dashboard on a restricted subdomain (`wp.empresa.es`) to manage news, landing pages and success stories. The public visitor of the corporate website, however, interacts with an ultra-fast static application compiled by Astro and distributed over Cloudflare's global network (`empresa.es`). --- ## 2. What Astro is and why it loads so fast Astro is a development framework built specifically for fast, content-driven corporate websites. Its approach differs radically from traditional [single-page application (SPA) frameworks](/en/headless-wordpress-nextjs-vs-astro-2026/) such as React or Angular, which ship large amounts of JavaScript to the visitor's browser and slow down the mid-range mobile devices common in the Spanish market. ### Astro islands architecture explained Astro uses an approach called the "islands architecture". By default, every page generated in Astro compiles to plain HTML and lightweight CSS, with all unnecessary JavaScript removed. If a section of the page needs interactivity (a dynamic search box, an interactive carousel or a product filter form), Astro creates an "island" of interactivity and injects the required framework code (React, Vue, Svelte or vanilla JS) only for that specific component. The result is flawless mobile performance: - **LCP (Largest Contentful Paint)**: the main visual element of the page (usually the hero image or the main title) appears in under 0.8 seconds on most mobile networks. - **INP (Interaction to Next Paint)**: user interactions with the page get an instant response, removing the lag typical of heavy websites. ### View Transitions and CSS animations in Astro In the 2026 corporate market, being fast is not enough; the site also has to delight the user visually. Astro has native support for the **View Transitions** API, which enables smooth navigation between pages that mimics a native mobile or desktop app. - Shared elements (the navigation menu, the logo or the header) stay in place while the page content fades smoothly. - It supports scroll-driven animations and optimised parallax effects that run directly on the device GPU without blocking the CPU main thread. The result is dynamic interfaces that respond naturally to touch and keep users on the site longer. ### How to serve WordPress images as AVIF in Astro Image weight is the main cause of slow loading on Spanish corporate websites. Astro ships native optimisation tools that process the files editors upload to the WordPress media library. According to the [Astro images guide](https://docs.astro.build/en/guides/images/), the transformation happens at build time for prerendered pages, and on the fly when a page rendered on demand is viewed. - **AVIF has to be requested**: it is not the default format. The [`astro:assets` reference](https://docs.astro.build/en/reference/modules/astro-assets/) states that `` produces a `.webp` file by default and that `` uses `['webp']` as its list of formats. To serve AVIF with a WebP fallback, set `formats={['avif', 'webp']}` on ``, or `format="avif"` on ``. - **Automatic sizing**: it generates several image sizes for different devices (responsive images), so a phone never downloads a high-resolution image meant for desktop screens. - **Authorised domains**: WordPress images arrive as remote URLs (`wp.empresa.es/wp-content/uploads/...`). Astro only optimises them if the domain is listed in `image.domains` or `image.remotePatterns` in `astro.config.mjs`. Images from unauthorised domains are displayed with no processing. - **Remote image dimensions**: Astro cannot know the size of a remote file in advance. Pass `width` and `height`, which the [REST API media endpoint](https://developer.wordpress.org/rest-api/reference/media/) already returns in `media_details`, or enable `inferSize`, which defaults to `false`. With the dimensions set, `` prevents layout shift (CLS). --- ## 3. Integrating the WordPress API with Astro Astro and WordPress communicate asynchronously through the CMS's native APIs. Depending on project complexity, development agencies in Spain choose one of two integration paths: ### A. WordPress REST API The REST API ships by default in WordPress core. It lets you query posts, pages, categories, tags and custom post types (CPT) through standard HTTP requests. It is the most mature, stable and simplest option for mid-sized projects. ```typescript // Ejemplo de consulta de posts de WordPress en Astro export async function getLatestPosts() { const res = await fetch('https://wp.empresa.es/wp-json/wp/v2/posts?_embed&per_page=10'); const posts = await res.json(); return posts.map(post => ({ title: post.title.rendered, slug: post.slug, content: post.content.rendered, featuredImage: post._embedded?.['wp:featuredmedia']?.[0]?.source_url })); } ``` ### B. WPGraphQL For large enterprise projects, WPGraphQL is the engineers' preferred option. It replaces the REST API with a [GraphQL query interface](/en/wordpress-rest-api-vs-graphql-2026-en/), fetching exactly the data required in a single call and avoiding the network overhead caused by redundant REST API queries (overfetching). ### C. How to map Gutenberg blocks to Astro components To preserve the design fidelity editors set up in the WordPress Gutenberg block editor, the Astro developer should not simply inject raw HTML with the `set:html` directive. Best practice is to parse the block content (with parsers such as `@wordpress/block-serialization-default-parser`) and map each Gutenberg block to an equivalent Astro component: - A `core/paragraph` block maps to an Astro `` component. - A `core/image` block maps to an Astro `` component that optimises the asset automatically. - Column blocks or dynamic grids are rendered with vanilla CSS Flexbox or Grid using optimised brand styles. This guarantees full style consistency and keeps redundant CSS out of the browser. ### D. How to load WordPress into Astro content collections The `getLatestPosts()` function above works, but it bypasses the tools Astro provides for content. The [content collections guide](https://docs.astro.build/en/guides/content-collections/) describes two models that fit a headless WordPress: - **Build-time collections**: defined with `defineCollection()` in `src/content.config.ts`. The built-in `glob()` and `file()` loaders read local files, so the WordPress API needs a custom loader that fetches the data and writes it to the Astro data store. Pages then query it with `getCollection()` and `getEntry()`. Astro suggests this model whenever possible, and it suits news, case studies and service pages that change a few times a day at most. - **Live collections**: defined with `defineLiveCollection()` in `src/live.config.ts`, they fetch data on every request through `getLiveCollection()` and `getLiveEntry()`. They need an adapter for on-demand rendering, such as the Cloudflare one, and Astro ships no built-in live loaders. The docs propose them for data that must be current on every visit, for example previewing CMS drafts. Live collections have limits worth knowing before you pick them: no MDX support, no image optimisation and, without caching, a call to the WordPress API on every visit. For a corporate website, a sensible split is a build-time collection for all published content and one live route only for editor previews. --- ## 4. How to deploy Astro on Cloudflare from Spain The choice of hosting infrastructure in Spain directly affects the speed of a corporate website and compliance with privacy regulation (AEPD). Deploying Astro on the **Cloudflare** edge network has become the standard solution for three operational reasons: ### 1. Cloudflare data centres and latency in Spain Cloudflare runs several data centres directly on the Iberian Peninsula (Madrid, Barcelona, Lisbon). Hosting the static frontend on **Cloudflare Pages** and routing dynamic functions through **Cloudflare Workers** means requests are answered with extremely low latency (often below 20ms time to first byte, TTFB). Visitors in Spain get the page from their own city, not from distant data centres in Central Europe or the United States. ### 2. Cloudflare GDPR compliance and EU data residency Unlike other platforms based on US servers, Cloudflare lets you configure data residency within the European Union and sign a Data Processing Agreement (DPA) that strictly complies with the EU General Data Protection Regulation (GDPR) and the requirements of the AEPD in Spain. This avoids legal trouble in privacy audits by corporate clients or in public tenders. ### 3. Serverless compute with Cloudflare Workers, KV and D1 When the corporate site needs complex dynamic features (private client portals, authentication, or secure processing of GDPR-compliant registration forms), development agencies in Spain use Cloudflare's edge ecosystem: - **Cloudflare Workers**: to run JavaScript code on the node closest to the user. - **Cloudflare KV**: an ultra-fast key-value store for user sessions, authorisation tokens or the WordPress API cache. - **Cloudflare D1**: a relational SQLite database distributed natively at the edge, which runs complex SQL queries in milliseconds without network latency. --- ## 5. SEO and GEO with Astro and headless WordPress Building with Astro and headless WordPress has a direct, positive impact on the two main traffic acquisition channels of the future in Spain: traditional search engines and assistants built on large language models (LLMs). …[truncated, fetch the canonical URL for the full body]… --- ## Google Maps integration in WordPress: A developer's guide URL: https://wppoland.com/en/google-maps-integration-wordpress-guide/ Description: Learn how to integrate Google Maps in WordPress programmatically. Optimize performance, secure API keys, and satisfy GDPR compliance requirements. Published: Wed Jun 18 Updated: Wed Oct 14 Type: guide Level: advanced Tags: google-maps, page-speed, gdpr, javascript-api # Google Maps Integration in WordPress: A Developer's Guide
Integrating Google Maps into WordPress websites is a common requirement for businesses, directory sites, and real estate portals. However, standard iframe embedding techniques introduce serious performance bottlenecks, privacy liabilities, and security risks. To build production-grade web applications in 2026, developers must prioritize asset loading optimization, enforce strict **General Data Protection Regulation (GDPR)** cookie controls, secure application API keys, and build robust fallback mapping architectures. This guide provides a detailed blueprint for optimizing Google Maps integrations in WordPress.
Learn more about our [WordPress development services](/en/wordpress-developer/) to construct high-performance integrations. --- ## 1. Why Google Maps iframe embeds hurt speed and GDPR compliance Copying and pasting the standard HTML ` ``` The Embed API suits a contact page with one location: no billing risk, no JavaScript to write, and native `loading="lazy"` on the iframe keeps it out of the initial page load. It does not remove the privacy question. The iframe still loads content from Google servers in the visitor's browser, so the consent rules in the GDPR sections of this guide apply to it as much as to the JavaScript API. When you need custom markers, clustering, your own styling or interaction with the rest of the page, the Maps JavaScript API is the tool. --- ## 3. How to restrict a Google Maps API key Before writing any mapping code, you must secure your credentials in the **Google Cloud Console**. An unrestricted API key can be scraped from your source code and used by unauthorized third parties, resulting in unexpected billing charges. ### Google Cloud Console key restriction steps 1. **Enable Constraints**: Navigate to Google Cloud Console > APIs & Services > Credentials. Select your Maps API key. 2. **Set Application Restrictions**: Under "Application restrictions," select **HTTP referrers (web sites)**. 3. **Configure Referrer Wildcards**: Add the exact domain paths for your environments: ``` https://wppoland.com/* https://www.wppoland.com/* https://staging.wppoland.com/* ``` 4. **Enforce API Restrictions**: Restrict the key to only run the **Maps JavaScript API** and **Geocoding API**. This prevents a compromise of the key from granting access to other Google Cloud services (such as Cloud Storage, translation APIs, or SQL databases) linked to the same billing account. 5. **Configure Quotas and Budget Alerts**: Set per-minute or per-day request quotas for each API under APIs & Services, so requests fail instead of billing when usage spikes, and add a budget alert in Cloud Billing (a budget only notifies, it does not stop the API). 6. **Separate Development and Production Credentials**: Always create separate API keys for your local staging environments and production instances. This allows you to apply tight domain referrer bounds on the production key while keeping development keys restricted to local domains (e.g., `http://localhost/*` or `http://127.0.0.1/*`), preventing security leaks across deployment pipelines. ### How to hide the API key behind a server-side REST proxy For highly sensitive endpoints (like geocoding queries), do not expose your API key to the client browser at all. Instead, route requests through a custom WordPress REST API proxy: ```php declare(strict_types=1); namespace WPPoland\API\Proxy; function register_maps_proxy_route(): void { register_rest_route( 'wppoland/v1', '/geocode', [ 'methods' => 'GET', 'callback' => __NAMESPACE__ . '\\handle_geocode_proxy', 'permission_callback' => '__return_true' ] ); } add_action( 'rest_api_init', __NAMESPACE__ . '\\register_maps_proxy_route' ); function handle_geocode_proxy( \WP_REST_Request $request ): \WP_REST_Response { $address = sanitize_text_field( $request->get_param( 'address' ) ); if ( empty( $address ) ) { return new \WP_REST_Response( [ 'error' => 'Missing address parameter' ], 400 ); } $api_key = get_option( 'wppoland_google_maps_secret_key', '' ); if ( empty( $api_key ) ) { return new \WP_REST_Response( [ 'error' => 'Server credentials misconfigured' ], 500 ); } // Call Google APIs server-to-server $url = sprintf( 'https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s', rawurlencode( $address ), esc_attr( $api_key ) ); $response = wp_remote_get( $url ); if ( is_wp_error( $response ) ) { return new \WP_REST_Response( [ 'error' => 'API query failed' ], 502 ); } $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); return new \WP_REST_Response( $data, 200 ); } ``` ### What to do when a Google Maps API key leaks A browser key is always visible in the page source, so "leak" usually means the restrictions were missing or too broad. Google's [API security best practices](https://developers.google.com/maps/api-security-best-practices) give the order of work: 1. Restrict first. Add an application restriction (websites, IP addresses, Android or iOS apps) and limit the key to the APIs it really calls. A website restriction accepts wildcards such as `https://*.example.com`, which covers every subdomain. 2. Use separate keys for separate apps and environments. If one key is compromised, you can delete or rotate it without touching the others. 3. Rotate only when restricting is not enough. Google advises regenerating keys "cautiously and only as a last resort". Rotation creates a new key with the old key's restrictions, and both keys work during the transition, so you can deploy the new key to WordPress before you retire the old one. 4. Before deleting a key, check in the metrics view that it has no successful traffic from production. In WordPress, store the key in an option or in `wp-config.php` and print it from one place, so a rotation is one change instead of a search through theme templates and page builder widgets. --- ## 4. How Google Maps Platform billing works after March 2025 Older tutorials mention a USD 200 monthly credit. That model is gone. According to Google's [March 2025 billing changes page](https://developers.google.com/maps/billing-and-pricing/march-2025) (accessed 2026-10-14), Google "has replaced the USD $200 monthly recurring credit with a free monthly usage threshold for each Core Services SKU". Each SKU belongs to a category (Essentials, Pro or Enterprise), and the free threshold differs by category. On the current [pricing page](https://developers.google.com/maps/billing-and-pricing/pricing), the SKUs a typical WordPress site uses sit in Essentials: Dynamic Maps (one map load in the Maps JavaScript API), Static Maps and Geocoding each have a free cap of 10,000 billable events per month, and the Embed SKU is listed as unlimited. Most Pro SKUs have a cap of 5,000 and most Enterprise SKUs 1,000. The thresholds count per SKU, so a site that loads a dynamic map and also geocodes addresses uses two separate allowances. Prices above the cap change over time; read them on the pricing page rather than from a blog post. A billing account is still required for the Maps JavaScript API, even if you never leave the free threshold. Two controls protect you from a surprise invoice. A budget in Cloud Billing sends alerts when spending reaches thresholds you set, but it does not stop the API. A quota in "APIs & Services" caps the number of requests per minute or per day, and requests above it fail. Google's [security best practices](https://developers.google.com/maps/api-security-best-practices) recommend per-minute, per-user quota limits for client-side projects. Set the quota above your real peak traffic, because legitimate visitors also get errors once it is reached. The same page from March 2025 gave Legacy status to three services: Places API, Directions API and Distance Matrix API. Legacy services get no new features, and the [legacy products page](https://developers.google.com/maps/legacy) says they "are not available in new Cloud projects". A project created today has to use Places API (New) and Routes API, so code samples that call `google.maps.places.Autocomplete` or `DirectionsService` may fail in a fresh project even though they still run on old ones. --- ## 5. Lazy load Google Maps with a map facade A Map Facade is a placeholder element that mimics a live map but loads only static assets. The heavy JavaScript libraries are requested only when a visitor clicks the placeholder, optimizing initial page load times. ### Map facade shortcode in PHP Add this code to your theme's `functions.php` file to register a map facade shortcode: ```php declare(strict_types=1); namespace WPPoland\Mapping; /** * Registers a shortcode to output a lightweight Map Facade. */ function render_map_facade_shortcode( array $atts ): string { $args = shortcode_atts( [ 'lat' => '54.5189', 'lng' => '18.5305', 'zoom' => '14', 'width' => '100%', 'height' => '400px', 'marker_title' => 'WPPoland Headquarters', 'static_fallback' => 'true' ], $atts ); …[truncated, fetch the canonical URL for the full body]… --- ## The rise of headless WooCommerce in Spain: payment gateways (Bizum, Redsys) and domestic logistics URL: https://wppoland.com/en/headless-woocommerce-spain-bizum-redsys-logistics/ Description: A complete guide to building a headless WooCommerce store for the Spanish market, covering Redsys, Bizum and integration with Spanish carriers. Published: Tue May 05 Updated: Wed Oct 14 Tags: woocommerce, headless, guide, 2026 # The rise of headless WooCommerce in Spain: payment gateways (Bizum, Redsys) and domestic logistics Spanish e-commerce has reached an advanced stage of technical maturity. In 2026, shoppers no longer judge only the product and the price: they expect an instant, secure buying experience built around local payment and shipping methods. Monolithic e-commerce platforms, where the server generates HTML and CSS in real time on every click, struggle to deliver the response times mobile users expect from a modern store. In answer to that need for performance and flexibility, **headless WooCommerce** has become established in Spain as the right choice for mid-sized and large retail brands. This article looks at where headless WooCommerce stands in Spain today, how to integrate the dominant payment gateways (Redsys and Bizum) successfully, and what it takes to coordinate invoicing and logistics for both the mainland and the islands. --- ## Benefits of headless WooCommerce for stores in Spain The headless model separates the WordPress admin panel and database engine (the backend) from the public interface customers interact with (the frontend). The MySQL database and WooCommerce business logic run on an optimised private server, while the public storefront is built with modern static generators such as Astro or Next.js and served through [global edge networks (CDNs)](/en/cloudflare-workers-wordpress-woocommerce-edge/). ### Headless WooCommerce speed on mobile in Spain Spain is one of the EU countries with the highest share of online shopping done on mobile devices. Mobile traffic often exceeds 70% of all visits to fashion and consumer goods retail stores. A slow site on a phone with a 4G connection or patchy coverage translates directly into checkout abandonment. With a [static frontend built on Astro](/en/headless-woocommerce-astro-ecommerce-performance-2026/), page weight drops sharply. Images are optimised to AVIF automatically and interactive JavaScript is limited to the dynamic elements (such as the add to cart button). This brings time to first byte (TTFB) below 20ms in Spain and makes a [PageSpeed score of 100/100](/en/core-web-vitals-100-score-case-study-2026/) achievable. ### Headless WooCommerce security under NIS2 Decoupling the static frontend from the WordPress installation hides the admin panel (`/wp-admin/`) and MySQL database queries from the public. Vulnerable backend plugins are no longer exposed on the frontend, which shrinks the attack surface, a useful argument for companies covered by the NIS2 cybersecurity directive ([Directive (EU) 2022/2555](https://eur-lex.europa.eu/eli/dir/2022/2555/oj)). --- ## How to integrate Redsys and Bizum with headless WooCommerce Any online store that wants to do well in Spain has to support card payments through Redsys and allow fast payments with Bizum. Bizum is convenient on mobile because the customer only enters a phone number and accepts the payment in a notification from their banking app ([Bizum, online purchases](https://bizum.com/es/compra-online/)). ### Why Redsys notifications fail in headless WooCommerce In a conventional WooCommerce installation, the official Redsys plugin transparently redirects the user to the bank's virtual point of sale (TPVV) and processes the payment confirmation through a background POST request that the Redsys server sends to WordPress (the online notification, or callback webhook). In a headless architecture, this flow raises two significant technical challenges: 1. **Redirect and return**: The user has to be redirected from the static frontend (e.g. `tienda.com`) to the Redsys TPVV and, once the payment is done, returned to a confirmation page on the frontend (`tienda.com/pago-confirmado/`), not to the internal WordPress URL (`backend.tienda.com`). 2. **Cloudflare firewall and blocked callbacks**: If the WordPress backend subdomain is protected by Cloudflare WAF (standard practice to prevent brute force attacks), the firewall can intercept the background notification that the Redsys servers send to confirm the transaction, flagging it as unwanted automated traffic. As a result, the customer's bank is charged, but the order status in WooCommerce stays at "Pending payment" and the confirmation email is never sent. ```mermaid sequenceDiagram participant Cliente as Customer browser participant Front as Astro frontend (tienda.com) participant Back as WordPress backend (wp.tienda.com) participant Redsys as Redsys / Bizum server Cliente->>Front: Starts payment Front->>Back: Creates order via REST API Back-->>Front: Returns Redsys signature and data Front->>Redsys: Redirects to the TPVV Cliente->>Redsys: Authorises payment (Bizum/card) Redsys-->>Back: Sends online notification (POST callback) Note over Back: Danger! Cloudflare WAF may block the Redsys POST Redsys->>Cliente: Return redirect after successful payment Cliente->>Front: Lands on /pago-confirmado/ Front->>Back: Checks order status ``` ### How to stop Cloudflare blocking Redsys callbacks To stop payment notifications from being blocked in decoupled architectures in Spain, development agencies use two main strategies: - **WAF exclusion rules**: Set up specific Cloudflare firewall rules that allow HTTP POST requests coming from the official Redsys subnets and aimed only at the WooCommerce callback endpoint. - **Cloudflare Workers router**: Build a lightweight serverless function (Worker) at the network layer that intercepts Redsys notifications on a public subdomain, validates the HMAC signature locally with the algorithm named in `Ds_SignatureVersion` and sends the order update command directly to the WooCommerce REST API over an internal, authenticated call. ```javascript // Ejemplo conceptual de validación de firma criptográfica de Redsys en un Worker export default { async fetch(request, env) { if (request.method !== "POST") { return new Response("Método no permitido", { status: 405 }); } const formData = await request.formData(); const ds_signature = formData.get("Ds_Signature"); const ds_merchantParameters = formData.get("Ds_MerchantParameters"); // Validar la firma HMAC con el algoritmo de Ds_SignatureVersion (HMAC_SHA512_V2 en la documentación actual de Redsys) const isValid = await checkRedsysSignature(ds_merchantParameters, ds_signature, env.REDSYS_KEY); if (!isValid) { return new Response("Firma no válida", { status: 400 }); } // Comunicar confirmación de pago al backend de WooCommerce de forma segura const response = await fetch(`${env.WP_API_URL}/wp-json/wppoland/v1/update-order-status`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${env.WP_API_TOKEN}` }, body: JSON.stringify({ parameters: ds_merchantParameters }) }); return new Response("OK", { status: 200 }); } }; ``` --- ## How to enable Bizum on a Redsys virtual POS Bizum is not contracted with Redsys but with the merchant's bank. Bizum's page for online stores sums up the process in three steps: talk to the bank about the terms and contract the service, integrate Bizum into the payment gateway, and place the button in the store ([Bizum, online payment method](https://bizum.com/es/metodo-de-pago-online/)). The same page notes that an agreement with a Bizum bank is always required first, including when the store uses a module or plugin from a bank gateway that is already certified. On a Redsys virtual POS the technical change is small. The guide published by Cajamar, one of the banks that sells this POS, says that after contracting the POS and the Bizum service the merchant must wait up to 48 hours for synchronisation and activation, and that the payment form is sent to `https://sis.redsys.es/sis/realizarPago` with the value `z` in the `DS_MERCHANT_PAYMETHODS` field ([Cajamar, Configura Bizum en tu TPV virtual](https://www.cajamar.es/storage/documents/6296/configura-bizum-en-tu-tpv-virtual-gcc.pdf)). Other banks may set different lead times. In a headless architecture, the WordPress backend adds that field to the signed parameters and the frontend only renders the button and the redirect form. From there the POS handles the authorisation. For the customer the flow is short. According to Bizum, they enter the phone number linked to Bizum, get a notification from their banking app, check it and accept the payment, then return to the merchant's site to finish the purchase ([Bizum, online purchases](https://bizum.com/es/compra-online/)). That return to the site does not prove the charge: the frontend `/payment-confirmed/` page must read the order status from WooCommerce, which only changes when the Redsys notification arrives. Bizum also supports recurring payments and subscriptions that the customer validates with their phone number the first time. ## How to verify a Redsys notification signature The current Redsys developer documentation describes the signature with `Ds_SignatureVersion` set to `HMAC_SHA512_V2` ([Redsys, Firmar una operación](https://pagosonline.redsys.es/desarrolladores-inicio/documentacion-operativa/firmar-una-operacion/)). The merchant's secret key is diversified per order with AES CBC encryption using an all-zero initialisation vector, that key is used to compute an HMAC SHA-512, and the result is encoded in Base64URL. To validate an online notification, Redsys lists these steps: 1. Decode the received Base64 parameters and read the order number, `Ds_Order`. 2. Compute the diversified key with that order number. 3. Compute the HMAC SHA-512 over the Base64 parameter exactly as it arrived, without decoding it. 4. Encode the result in Base64URL and compare it with `Ds_Signature`. Only process the message if they match. If the Worker signs the already decoded JSON, the computed signature will not match `Ds_Signature` even when the notification is genuine. Many existing libraries and plugins still use the previous version, `HMAC_SHA256_V1`, so the Worker should read the `Ds_SignatureVersion` of each notification and validate with the matching algorithm instead of hardcoding one. Once the signature checks out, the Worker also confirms that the amount and order number match the WooCommerce order before marking it as paid. --- ## How to set up WooCommerce shipping in Spain Delivery is a deciding factor in Spanish e-commerce. Brands in Spain need automatic connections to the leading domestic carriers (such as Correos Express, SEUR, GLS or MRW) to speed up warehouse operations. ### How to validate Spanish postal codes at checkout To avoid sorting errors in the dispatch warehouse and extra charges for rerouted parcels, the store checkout must validate Spanish postal codes strictly: - **Format**: 5 numeric digits. - **Regional ranges**: The first two digits identify the destination province (e.g. `08` for Barcelona, `28` for Madrid, `35` and `38` for the Canary Islands). - **Rate zones**: The shipping cost calculation in the headless checkout must clearly distinguish between mainland Spain, the Balearic Islands, the Canary Islands, and Ceuta and Melilla. …[truncated, fetch the canonical URL for the full body]… --- ## Video background performance, autoplay and Core Web Vitals URL: https://wppoland.com/en/performance-video-backgrounds-autoplay-guide-en/ Description: Master video background performance in 2026. Learn video format optimization, hosting strategies, lazy loading, accessibility, and Core Web Vitals compliance for fast hero videos. Published: Wed May 11 Updated: Wed Oct 14 Type: guide Level: advanced Tags: youtube, vimeo, lazy-loading, core-web-vitals, video-optimization, web-performance, accessibility ## Do background videos slow down a website Learn more about [WordPress speed optimization](/en/speed-up-wordpress/) at WPPoland. A looping hero video can sell atmosphere that a still image cannot. It can also delay [Largest Contentful Paint (LCP)](/en/core-web-vitals-2026-inp-optimization-guide/), burn mobile data, and leave a frozen poster while the browser refuses autoplay. The failure mode is not subtle: visitors see a blank or half-loaded hero, then bounce before the copy loads. Treat background video as a progressive enhancement. The page must read and convert with the poster alone. Motion is optional decoration that arrives after the critical path is clear. This guide covers autoplay policies, poster versus LCP tradeoffs, `preload` choices, `prefers-reduced-motion`, WordPress media-library traps, mobile data cost, and the cases where you should skip background video entirely. ## How to make a background video autoplay Browsers block unexpected sound. Autoplay with audio is restricted across Chromium, Safari, and Firefox. Background loops almost always need all four attributes together: - `autoplay` - request playback without a user gesture - `muted` - no audio track energy in the first paint window - `playsinline` - keep playback in-page on iOS instead of forcing fullscreen - `loop` - restart when the clip ends (typical for decorative heroes) MDN documents the autoplay rules and the muted exception: muted media is more likely to start without a gesture, while audible autoplay usually waits for interaction. iOS Safari is especially strict about `playsinline`. Drop it and the clip may jump to fullscreen or never start as a background. Do not rely on `autoplay` alone. Pair it with `muted` and `playsinline`, then handle the `play()` promise. If the browser rejects playback, keep the poster visible and do not throw an unhandled rejection into the console. ```html ``` For third-party players the same rules apply through query params: mute, hide controls, and (for YouTube looping) a playlist id. Those embeds still pull player JavaScript. Prefer a self-hosted `