WordPress + Avada Builderhidden Base64 content
Dry-runpreview before updating
Targeted changeonly what actually changes

Bulk Replace Text Hidden in Avada Base64 Blocks (WordPress + Python)

Older WordPress sites built with Avada can store parts of page content inside ... as Base64. That means the text you see on the page may not exist as plain text in the database. Standard search and replace tools often cannot touch it. This guide shows a safer workflow using a dry-run-capable Python script to decode, replace, re-encode, and optionally update the database.

Need help? Contact us

Practical use case: brand renames, vendor swaps, platform references, or legacy punctuation cleanup across dozens or hundreds of Avada pages.

Why normal search and replace tools struggle with Avada Base64 content

The key limitation is not "safety", it is visibility. When Avada stores content as Base64 inside , the words you want to replace are effectively hidden from plain-text search. Most WordPress tools only replace what is stored as readable strings, they do not decode Base64, modify it, then re-encode it.

This is why you can search the database for a term and get zero results, while the term still shows on the page. It is not "missing", it is encoded.

Translation: standard tools can be safe but incomplete when content is stored in Base64 builder blocks.

Common symptoms

  • You search the database for a term, nothing shows up, but you still see it on the page.
  • A search and replace plugin reports "0 changes", yet the website still contains the old branding.
  • You can only edit it by opening every page in Avada Builder, which is painful at scale.

What the Python approach does differently

  • Find only rows that contain .
  • Extract Base64 payloads, decode them to plain text.
  • Apply your rename rules, then re-encode to Base64.
  • Optionally update the database, only when a decoded block actually changed.

Step-by-step: safe bulk rename inside Avada

Always take a full database backup before any bulk update. Test on a clone first.

In production, schedule a maintenance window, apply changes, then verify front-end rendering and builder editing.

1) Decide what you are replacing

  • Domain name changes: "oldcompany.com""newcompany.ca"
  • Phone number updates: "(604) 555-1234""778-555-5678"
  • Email address changes: "[email protected]""[email protected]"
  • Company name rebranding: "ABC Services Ltd.""XYZ Solutions Inc."
  • URL updates: "http://oldsite.com""https://newsite.ca"
  • Legacy contact information inside Avada Code Blocks

2) Run a dry-run first (no database updates)

Dry-run prints which post IDs are affected and shows short before/after previews from decoded content. You review the output first, then decide whether to apply.

Terminal
python3 fuse_b64_update.py

3) Apply changes (updates the database)

Terminal
python3 fuse_b64_update.py --apply

4) Validate after running

  • Spot-check a few updated pages on the front end.
  • Open the same pages in Avada Builder, verify the Code Block renders correctly.
  • Clear caches (plugin cache, server cache, CDN) and test again.

Beautified, sanitized script (no credentials in the file)

This version keeps your original logic, but removes hard-coded credentials, improves readability, adds safer defaults, and highlights exactly where readers customize their replacements.

Readers edit REPLACEMENTS only. Credentials come from environment variables.

Examples: "oldcompany.com": "newcompany.ca", "(604) 555-1234": "778-555-5678", "[email protected]": "[email protected]"

fuse_b64_update.py (sanitized)
#!/usr/bin/env python3
"""
fuse_b64_update.py

Purpose:
  Find Avada ... Base64 blocks in wp_posts.post_content,
  decode them, apply string replacements, re-encode them, and optionally update rows.

Why:
  Many WordPress search/replace tools cannot replace text hidden inside Base64 payloads
  because the text is not stored as plain strings in the database.

Usage:
  # Dry run (report only)
  python3 fuse_b64_update.py

  # Apply changes (perform DB updates)
  python3 fuse_b64_update.py --apply

Credentials:
  Use environment variables instead of hardcoding:
    WP_DB_HOST, WP_DB_USER, WP_DB_PASS, WP_DB_NAME
Optional overrides:
    WP_DB_TABLE, WP_DB_COLUMN, WP_DB_ID_COLUMN
"""

from __future__ import annotations

import argparse
import base64
import os
import re
import sys
from dataclasses import dataclass
from typing import List, Tuple

import pymysql


# =========================
# Configuration (safe)
# =========================

DB_HOST = os.getenv("WP_DB_HOST", "localhost")
DB_USER = os.getenv("WP_DB_USER", "")
DB_PASS = os.getenv("WP_DB_PASS", "")
DB_NAME = os.getenv("WP_DB_NAME", "")

TABLE = os.getenv("WP_DB_TABLE", "wp_posts")
COLUMN = os.getenv("WP_DB_COLUMN", "post_content")
ID_COLUMN = os.getenv("WP_DB_ID_COLUMN", "ID")

# EDIT THIS ONLY:
# Mapping of "find" -> "replace"
REPLACEMENTS = {
    # Domain name changes (most common):
    # "oldcompany.com": "newcompany.ca",
    
    # Phone number updates:
    # "(604) 555-1234": "778-555-5678",
    
    # Email address changes:
    # "[email protected]": "[email protected]",
    
    # Company name rebranding:
    # "ABC Services Ltd.": "XYZ Solutions Inc.",
    
    # Full URL updates:
    # "http://oldsite.com": "https://newsite.ca",
}

# Regex to capture Base64 inside ...
FUSION_CODE_RE = re.compile(
    r"(\\)\\s*([A-Za-z0-9+/=\\r\\n]+?)\\s*(\\[/fusion_code\\])",
    re.DOTALL | re.IGNORECASE,
)

# Limit number of sample previews per row (avoid huge output)
SAMPLES_PER_ROW = 3
SNIPPET_LEN = 260


@dataclass
class Preview:
    before: str
    after: str


def decode_b64(b64_text: str) -> Tuple[str, bool]:
    """Decode Base64 safely. Returns (decoded_text, success)."""
    try:
        clean = "".join(b64_text.split())
        decoded_bytes = base64.b64decode(clean, validate=False)
        decoded = decoded_bytes.decode("utf-8", errors="strict")
        return decoded, True
    except Exception:
        return "", False


def encode_b64(text: str) -> str:
    """Encode text to Base64 without newlines."""
    return base64.b64encode(text.encode("utf-8")).decode("ascii")


def apply_replacements(decoded: str) -> Tuple[str, bool]:
    """Apply REPLACEMENTS mapping. Returns (new_text, changed)."""
    new_text = decoded
    for old, new_val in REPLACEMENTS.items():
        new_text = new_text.replace(old, new_val)
    return new_text, (new_text != decoded)


def process_content(content: str) -> Tuple[str, int, List[Preview]]:
    """
    Process post content:
      - find  Base64 blocks
      - decode
      - apply replacements
      - re-encode
    Returns:
      (new_content, number_of_changed_blocks, previews)
    """
    changes = 0
    previews: List[Preview] = []

    def replacer(match: re.Match) -> str:
        nonlocal changes, previews

        prefix, b64payload, suffix = match.group(1), match.group(2), match.group(3)

        decoded, ok = decode_b64(b64payload)
        if not ok:
            return match.group(0)  # leave unchanged

        new_decoded, changed = apply_replacements(decoded)
        if not changed:
            return match.group(0)

        changes += 1

        if len(previews) < SAMPLES_PER_ROW:
            before_snip = decoded[:SNIPPET_LEN].replace("\\n", "\\\\n")
            after_snip = new_decoded[:SNIPPET_LEN].replace("\\n", "\\\\n")
            previews.append(Preview(before=before_snip, after=after_snip))

        new_b64 = encode_b64(new_decoded)
        return f"{prefix}{new_b64}{suffix}"

    new_content = FUSION_CODE_RE.sub(replacer, content or "")
    return new_content, changes, previews


def require_db_settings() -> None:
    missing = []
    if not DB_USER:
        missing.append("WP_DB_USER")
    if not DB_NAME:
        missing.append("WP_DB_NAME")
    # password can be blank in some local setups, so we do not force it
    if missing:
        print("ERROR: Missing DB settings:", ", ".join(missing))
        print("Set env vars: WP_DB_HOST, WP_DB_USER, WP_DB_PASS, WP_DB_NAME")
        sys.exit(1)


def connect_db():
    try:
        return pymysql.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASS,
            database=DB_NAME,
            charset="utf8mb4",
        )
    except Exception as e:
        print("ERROR: could not connect to database:", e)
        sys.exit(1)


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Decode Avada  Base64 blocks and apply replacements."
    )
    parser.add_argument(
        "--apply",
        action="store_true",
        help="Apply updates to the database (default: dry-run only).",
    )
    args = parser.parse_args()

    require_db_settings()

    mode = "APPLY MODE" if args.apply else "DRY RUN MODE"
    print(f"{mode} | Replacements: {REPLACEMENTS}")

    conn = connect_db()

    total_rows = 0
    total_rows_changed = 0
    total_blocks_changed = 0

    try:
        with conn.cursor() as cur:
            cur.execute(
                f"SELECT {ID_COLUMN}, {COLUMN} FROM {TABLE} WHERE {COLUMN} LIKE %s",
                ("%%",),
            )
            rows = cur.fetchall()
            total_rows = len(rows)
            print(f"Found {total_rows} rows containing ...")

            for row_id, content in rows:
                new_content, blocks_changed, previews = process_content(content or "")
                if blocks_changed <= 0:
                    continue

                total_rows_changed += 1
                total_blocks_changed += blocks_changed

                print("-" * 80)
                print(f"Row ID: {row_id} | Changed fusion_code blocks: {blocks_changed}")

                for i, pv in enumerate(previews, start=1):
                    print(f" Preview #{i} (truncated):")
                    print("  BEFORE:", pv.before)
                    print("  AFTER: ", pv.after)
                    print()

                if args.apply:
                    cur.execute(
                        f"UPDATE {TABLE} SET {COLUMN} = %s WHERE {ID_COLUMN} = %s",
                        (new_content, row_id),
                    )
                    print(f" -> Row {row_id} UPDATED.")
                else:
                    print(f" -> DRY RUN: no DB update performed for row {row_id}.")

            if args.apply:
                conn.commit()

    finally:
        conn.close()

    print("=" * 80)
    print(f"Rows scanned: {total_rows}")
    print(f"Rows with changes: {total_rows_changed}")
    print(f"Total fusion_code blocks changed: {total_blocks_changed}")
    print("Done.")


if __name__ == "__main__":
    main()

Environment variables example (recommended)

Terminal
export WP_DB_HOST="localhost"
export WP_DB_USER="your_db_user"
export WP_DB_PASS="your_db_password"
export WP_DB_NAME="your_db_name"

python3 fuse_b64_update.py
python3 fuse_b64_update.py --apply

Suggested tags: Avada, Avada Builder, Fusion Builder, fusion_code, Base64, WordPress, wp_posts, bulk replace, Python, pymysql.

FAQ

Is this "encryption" or "encoding"?

Base64 is technically encoding, not cryptographic encryption. But operationally it behaves like "encrypted content" for search and replace, because the words are not present in plain text inside the database. Tools cannot match what they cannot see.

What happens if a Base64 block cannot be decoded?

The script skips that block and leaves it unchanged. This avoids corrupting content that is not valid UTF-8 or is not a standard Base64 payload.

Can I do multiple replacements at once?

Yes. Add multiple entries to REPLACEMENTS. If terms overlap, replace the most specific terms first.

Need help with your Avada migration or bulk updates?

If you're dealing with complex Avada Base64 content or need assistance with WordPress migrations, our team can help. Fill out the form below and we'll get back to you.