Fix Optimizely CMS 11 WAF Blocking with Base64 Encoding

Fix Optimizely CMS 11 WAF Blocking with Base64 Encoding

Valerie Gaudette
Valerie Gaudette
March 4, 2026
Last updated : March 5, 2026
March 4, 2026

Your content editor clicks save. The page shows an error. Another WAF block.

This frustrating scenario plays out daily for teams running Optimizely CMS 11 behind Web Application Firewalls. Optimizely CMS 11 WAF blocking is one of the most common issues we encounter with enterprise deployments. The editor was just trying to add a tracking script or embed some HTML, but the WAF flagged it as a potential XSS attack and killed the request.

This guide walks through why this happens, how to implement a Base64 encoding workaround, and when you should consider alternative approaches instead.

What This Guide Covers

We'll explore the technical reasons behind WAF blocking in CMS environments, then build a custom property type that encodes content before submission. You'll get working code examples for both server-side and client-side components. We'll also discuss when this approach makes sense versus when you should push for proper WAF rule adjustments. This tutorial will show you how to bypass WAF blocking Optimizely CMS editors encounter daily.

Prerequisites

Before implementing this workaround, you'll need:

  • Optimizely CMS 11 running on .NET Framework 4.7.x
  • Understanding of custom property types in Optimizely
  • Access to modify your project's codebase
  • Basic familiarity with the Dojo-based editing interface
  • Permission to deploy changes (obviously)

This guide covers Optimizely CMS custom property type development in detail. You should also have a clear picture of which WAF is blocking your requests. Common culprits include Azure Application Gateway, AWS WAF, Cloudflare, and ModSecurity-based setups.

Why WAFs Block Your CMS Content

Web Application Firewall CMS blocking occurs because WAFs inspect HTTP traffic and block requests that match attack signatures. The problem? Legitimate CMS content often contains the same patterns as actual attacks. WAF false positives CMS content triggers are extremely common in editorial workflows.

When your editor saves content containing <script> tags, event handlers like onclick, or embedded code samples, the WAF sees patterns that look like XSS attacks. SQL-like syntax in content triggers injection detection rules. Even base64-encoded images can raise flags because many WAFs automatically decode Base64 and scan the result.

Common rules that get triggered include:

  • Rule 941100: XSS Attack Detected via libinjection
  • Rule 941110: XSS Filter Category 1
  • Rule 942100: SQL Injection Attack Detected via libinjection
  • Rule 949110: Inbound Anomaly Score Exceeded

Finding the right Optimizely CMS 11 XSS blocking fix depends on which specific rules are triggering. Working with various companies we've learned that marketing teams embedding third-party tracking codes cause the most WAF friction. They're adding legitimate business functionality, but the WAF can't distinguish their Google Tag Manager snippet from a malicious script injection.

The Base64 Encoding Approach

The workaround is straightforward in concept: encode content to Base64 before it leaves the browser, then decode it when it reaches the server. Since the HTTP request body contains encoded content instead of raw HTML or script tags, the WAF's pattern matching doesn't find anything suspicious. Base64 encoding WAF bypass is a proven technique for handling these scenarios.

Here's how the pieces fit together:

  • Custom Dojo widget encodes content before form submission
  • Encoded data travels through the WAF without triggering rules
  • Custom property type on the server decodes back to original content
  • Content is stored normally in the database
  • Editors never see the encoding process

Step-by-Step Implementation

Step 1: Create the Custom Property Class

Start with a custom property that handles the Base64 decoding. This extends the standard PropertyXhtmlString to intercept values as they're set and retrieved. This PropertyXhtmlString custom implementation handles all the encoding logic transparently.

using System;
using System.Text;
using EPiServer.Core;
using EPiServer.PlugIn;

namespace YourProject.Properties
{
    [PropertyDefinitionTypePlugIn]
    public class EncodedXhtmlStringProperty : PropertyXhtmlString
    {
        public override object Value
        {
            get => base.Value;
            set => base.Value = DecodeIfNeeded(value);
        }

        private object DecodeIfNeeded(object value)
        {
            if (value is string stringValue && IsBase64Encoded(stringValue))
            {
                try
                {
                    var bytes = Convert.FromBase64String(stringValue);
                    return Encoding.UTF8.GetString(bytes);
                }
                catch (FormatException)
                {
                    return value;
                }
            }
            return value;
        }

        private bool IsBase64Encoded(string value)
        {
            if (string.IsNullOrEmpty(value))
                return false;

            // Check for Base64 marker prefix you'll add client-side
            return value.StartsWith("B64:");
        }
    }
}

Note the B64: prefix check. Adding a marker to your encoded content makes detection reliable without accidentally decoding content that happens to be valid Base64.

Step 2: Build the Editor Descriptor

The editor descriptor tells Optimizely to use your custom JavaScript editor for this property type. This Optimizely editor descriptor tutorial covers the essential configuration needed.

using System;
using System.Collections.Generic;
using EPiServer.Cms.Shell.UI.ObjectEditing.EditorDescriptors;
using EPiServer.Core;
using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

namespace YourProject.EditorDescriptors
{
    [EditorDescriptorRegistration(
        TargetType = typeof(XhtmlString), 
        UIHint = "EncodedXhtml")]
    public class EncodedXhtmlEditorDescriptor : XhtmlStringEditorDescriptor
    {
        public override void ModifyMetadata(
            ExtendedMetadata metadata, 
            IEnumerable<Attribute> attributes)
        {
            base.ModifyMetadata(metadata, attributes);
            metadata.ClientEditingClass = "yourproject/editors/EncodedXhtmlEditor";
        }
    }
}

The XhtmlString editor descriptor configuration above connects your custom property to the client-side component.

Step 3: Create the Client-Side Editor

This Dojo widget wraps the standard XHTML editor and intercepts the value before submission. The Dojo widget Optimizely CMS 11 uses follows the AMD module pattern.

Create this file at /ClientResources/Scripts/editors/EncodedXhtmlEditor.js:

define([
    "dojo/_base/declare",
    "dojo/_base/lang",
    "epi-cms/contentediting/editors/XhtmlStringEditor"
], function (declare, lang, XhtmlStringEditor) {

    return declare([XhtmlStringEditor], {

        _setValueAttr: function (value) {
            // Decode for display if it's encoded
            if (value && typeof value === "string" && value.indexOf("B64:") === 0) {
                try {
                    var encoded = value.substring(4);
                    value = decodeURIComponent(escape(atob(encoded)));
                } catch (e) {
                    console.warn("Failed to decode Base64 value", e);
                }
            }
            this.inherited(arguments, [value]);
        },

        _getValueAttr: function () {
            var value = this.inherited(arguments);
            
            if (value && typeof value === "string" && value.length > 0) {
                try {
                    var encoded = btoa(unescape(encodeURIComponent(value)));
                    return "B64:"   encoded;
                } catch (e) {
                    console.warn("Failed to encode value to Base64", e);
                    return value;
                }
            }
            return value;
        }
    });
});

Step 4: Register the Client Resources

Add the module path to your module.config:


<module>
  <dojo>
    <paths>
      <add name="yourproject" path="Scripts" />
    </paths>
  </dojo>
</module>

Step 5: Apply the UIHint to Your Properties

Now use the custom editor on any property that needs WAF bypass:

public class ArticlePage : PageData
{
    [UIHint("EncodedXhtml")]
    [Display(Name = "Body Content")]
    public virtual XhtmlString MainBody { get; set; }
    
    [UIHint("EncodedXhtml")]
    [Display(Name = "Tracking Scripts")]
    public virtual XhtmlString TrackingCode { get; set; }
}

Our team found that applying this selectively works better than enabling it globally. Only use it on properties where editors regularly hit WAF blocks.

Handling Plain String Properties

Sometimes you need to encode plain strings rather than XHTML content. The approach is similar but simpler:

[BackingType(typeof(PropertyLongString))]
public class EncodedStringProperty : PropertyLongString
{
    public override object Value
    {
        get => DecodeValue(base.Value);
        set => base.Value = value;
    }

    private object DecodeValue(object value)
    {
        if (value is string str && str.StartsWith("B64:"))
        {
            try
            {
                var encoded = str.Substring(4);
                var bytes = Convert.FromBase64String(encoded);
                return Encoding.UTF8.GetString(bytes);
            }
            catch
            {
                return value;
            }
        }
        return value;
    }
}

Use this for JWT tokens, serialized JSON, or other string data that triggers WAF rules.

Common Mistakes to Avoid

Forgetting the prefix marker. Without a clear indicator like B64:, your decoder might try to decode content that was already plain text but happened to look like valid Base64. This corrupts data silently.

Encoding on the server instead of client. If you encode in server-side code before the HTTP response, you haven't solved anything. The WAF inspects the request coming from the browser, not the response going back. The encoding must happen in JavaScript before form submission.

Applying to all properties blindly. Extra encoding overhead on every property slows down the editing experience. Apply the UIHint only where needed.

Not handling decode failures gracefully. Content migrated from before your implementation won't have the prefix. Make sure your decoder falls back to returning the original value when decoding fails.

Skipping the security conversation. This workaround exists because the WAF is doing its job. Before implementing, talk with your security team. They may prefer to tune WAF rules rather than bypass them.

Testing and Verification

Verify Encoding Works

  • Open browser developer tools
  • Navigate to a page with your encoded property
  • Edit the content and add some HTML that previously triggered blocks (like a <script> tag)
  • Watch the Network tab when you save
  • Confirm the request body shows B64: followed by encoded content
  • Verify the save completes without WAF errors

Verify Decoding Works

  • Save content with the encoded property
  • Query the database directly and confirm the stored value is decoded (not Base64)
  • Reload the page in edit mode and confirm the content displays correctly
  • Check the published site to ensure content renders properly

Test Edge Cases

  • Empty content
  • Very long content (megabytes of text)
  • Content with Unicode characters
  • Content with existing Base64 data (like embedded images)
  • Legacy content created before this implementation

When to Use This Approach (And When Not To)

This encoding workaround makes sense when:

  • Your security team won't or can't modify WAF rules
  • You need a quick fix while proper rule tuning is negotiated
  • Specific properties consistently trigger false positives
  • You're working with a managed WAF you don't control

We recommend against this approach when:

  • You can configure WAF rule exclusions for CMS paths
  • Your environment requires strict compliance (PCI, SOC2, ISO)
  • You have the access to whitelist editor IP ranges
  • You're planning to migrate to CMS 12 soon anyway

The better long-term fix is usually WAF configuration. Path-based exclusions for /episerver/ or /EPiServer/CMS/Content/ paths, combined with IP restrictions for editors, solve the problem without custom code. Azure WAF Optimizely configuration and ModSecurity CMS rule exceptions are both viable paths depending on your infrastructure.

Security Implications

This approach does reduce defense depth. WAFs provide a layer of protection even for authenticated users, and bypassing that layer means any malicious content an authenticated user submits won't be caught.

For most teams, this tradeoff is acceptable because:

  • Edit mode already requires authentication
  • Editors are trusted internal users
  • Content is reviewed before publishing

Document your implementation and include it in security reviews. Log all content submissions so you have an audit trail. And keep pushing your security team for proper WAF rule adjustments as the permanent fix.

Looking Ahead: CMS 12 Migration

Optimizely CMS 11 is now considered legacy, with mainstream support ending in late 2024. CMS 12 runs on .NET 6 and has a different architecture that may handle WAF interactions differently. Optimizely CMS 12 migration WAF considerations should be part of your upgrade planning.

If you're planning a migration anyway, you might skip implementing this workaround and focus that effort on the upgrade instead. The WAF issues may resolve themselves with the new platform, or at minimum you'll want to re-evaluate your approach with the new architecture.

Wrapping Up

WAF blocking in Optimizely CMS 11 is a real problem that disrupts editor workflows. The Base64 encoding approach gives you a working fix by encoding content before it hits the WAF and decoding it server-side.

The implementation involves a custom property type, an editor descriptor, and a client-side Dojo widget. Apply it selectively to properties that trigger false positives, not globally. Test thoroughly, especially edge cases around existing content and Unicode.

Remember that this is a workaround, not a permanent fix. Keep pushing for proper WAF rule exclusions that recognize CMS traffic patterns.

If your team is dealing with WAF friction in Optimizely or considering a CMS 12 migration, we can help you evaluate the right approach for your specific environment and security requirements. Reach out to discuss your situation.

Share this article