RG Digital Logo
RG DIGITAL · INSIGHTS · DIGITAL ARCHITECTURE

NGINX Cache Collision in Multilingual WordPress

How a custom WordPress language architecture exposed a cache-layer collision in production — and why solving it required looking beyond the application itself.

A technically correct application can still fail before WordPress executes a single line of its logic.

We discovered exactly that while validating the bilingual architecture of RG Digital.

Instead of adopting a general-purpose multilingual plugin, we designed the language architecture ourselves and developed RG Language Bridge, a custom WordPress plugin responsible for language ownership, editorial relationships, hreflang generation, schema language, routing and deterministic language-switching behavior.

The application was behaving correctly. The plugin was reading language preferences correctly. Destination pages were resolving correctly. The expected HTTP status codes were being generated.

Yet a visitor who had explicitly selected Spanish could still be redirected to the English version of the site.

The problem was not the language architecture.
The problem existed one layer before it.

01 · THE ARCHITECTURAL DECISION

Why We Built Our Own Multilingual Layer

WordPress already has mature multilingual solutions such as WPML and Polylang. They solve broad translation and multilingual publishing requirements for thousands of different use cases.

Our requirement was narrower and more architectural.

We did not need a translation management platform to determine how the site should behave. We needed a small application layer that implemented a contract we could define, inspect, test and control ourselves.

The architecture required independent language URLs, explicit editorial relationships between equivalent pages, predictable hreflang output, controlled language routing and deterministic fallback behavior.

CONTROL

Explicit Architecture

Language behavior follows an application contract designed specifically for the site instead of being determined by the internal model of a general-purpose multilingual platform.

SCOPE

Only What We Need

The system handles language ownership, editorial pairing, hreflang, schema language, switching and routing without introducing a larger translation-management layer.

OWNERSHIP

Predictable Behavior

Every redirect, fallback and relationship exists in our own codebase and can be audited against a clearly defined architectural contract.

We did not need another translation layer. We needed a language architecture.

That decision led to the development of RG Language Bridge.

02 · INSIDE RG LANGUAGE BRIDGE

A Small Plugin With an Explicit Contract

RG Language Bridge does not automatically translate pages.

English and Spanish remain independently authored experiences. The plugin creates and enforces the relationships required for those experiences to behave as one coherent multilingual system.

RG LANGUAGE BRIDGE rg_language taxonomy → identifies page language paired_page_id → connects true editorial equivalents hreflang → en-us ↔ es-mx → x-default = EN member of the same editorial pair schema language → en → en-US → es → es-MX language switcher → direct pair → paired ancestor → language Home root router → no preference: 301 → /en/ → EN preference: 302 → /en/ → ES preference: 302 → /es/

Each responsibility is intentionally small.

Rank Math remains responsible for canonical URLs, sitemap generation and the broader SEO layer. RG Language Bridge only owns the multilingual responsibilities that belong to its architectural scope.

03 · EDITORIAL RELATIONSHIPS

Structural Symmetry Is Not Editorial Equivalence

One of the most important rules inside RG Language Bridge is that pages are not paired merely because they occupy similar positions in the English and Spanish site structures.

A pairing exists only when two pages represent the same editorial content for different language audiences.

VALID EDITORIAL PAIR /en/services/ ↔ /es/servicios/ NOT AUTOMATICALLY A VALID PAIR /en/case-studies/ Featured project for the U.S. market /es/casos-de-estudio/ Featured project for the Mexican market

The second example may be structurally symmetrical, but the content is not equivalent.

In that situation, the pages remain intentionally unpaired and RG Language Bridge does not generate an artificial hreflang relationship between them.

Multilingual architecture should represent content relationships that actually exist, not relationships that merely look convenient in a URL tree.

04 · LANGUAGE RESOLUTION

The Switcher Resolves Intent Instead of Guessing URLs

The language switcher does not mechanically replace /en/ with /es/ or assume that an equivalent URL must exist.

It follows a deterministic resolution sequence.

LANGUAGE SWITCHER RESOLUTION 01 · DIRECT PAIR Equivalent editorial page exists → use that page 02 · PAIRED ANCESTOR No direct pair exists → find the nearest paired ancestor 03 · LANGUAGE HOME No meaningful paired ancestor exists → use the destination language Home

This allows the system to preserve visitor intent without fabricating relationships between content that is not genuinely equivalent.

For example, an unpaired Case Studies page can resolve through its paired Home ancestor and send the visitor to the appropriate language entry point.

05 · THE ROOT ROUTER

One URL With Multiple Valid Application States

The English and Spanish experiences live under independent language paths:

/en/ → English /es/ → Spanish

The root URL has a different responsibility.

It is not a content page. It acts as the entry router for the multilingual architecture.

NO PREFERENCE

Default Route

A visitor without a stored language preference receives the English Home as the default destination.

EN PREFERENCE

Explicit English

A visitor who explicitly selected English keeps that preference through a temporary redirect.

ES PREFERENCE

Explicit Spanish

A visitor who selected Spanish is routed directly into the Spanish experience.

No language preference / → 301 → /en/ rg_lang_pref=en / → 302 → /en/ rg_lang_pref=es / → 302 → /es/

During application testing, that contract behaved exactly as designed.

Then production behaved differently.

06 · PRODUCTION VALIDATION

The First Suspect Was Our Own Code

During production validation, a visitor with an explicit Spanish preference could still receive the permanent English redirect from the root URL.

Cookie: rg_lang_pref=es Expected: 302 → /es/ Observed: 301 → /en/

Because the routing component was software we had developed, the first question was obvious:

Was there a defect in RG Language Bridge?

Before blaming the hosting environment or introducing additional infrastructure changes, we audited our own application layer.

Redirect logic, cookie handling, language validation and routing behavior were reviewed against the architecture contract.

Then we moved from static code review to runtime isolation.

07 · ISOLATING THE APPLICATION

A Unique Request Changed the Investigation

We repeated the request using a unique query string that could not already exist as the same cached request.

/?rg_probe=es-001 Cookie: rg_lang_pref=es

This time the response was exactly what the application contract required.

HTTP/1.1 302 Found Location: /es/ Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private

The same test with an explicit English preference also behaved correctly.

rg_lang_pref=en → 302 → /en/

RG Language Bridge was reading the cookie correctly and producing the correct routing decision.

Something else was returning the incorrect response before that application logic had an opportunity to execute.

08 · THE CACHE COLLISION

When the Cache Considered Different Requests Equivalent

We identified NGINX server caching as the next variable and reproduced the failure in a controlled sequence.

First, the cache was cleared.

NGINX CACHE ACTIVE
Cookie ES
302 → /es/ ✓
No cookie
301 → /en/ ✓
Cookie ES again
301 → /en/ ✕
APPLICATION CONTRACT EXPECTED
Cookie ES
302 → /es/
No cookie
301 → /en/
Cookie ES again
302 → /es/

The second request generated the legitimate default 301 → /en/ response.

NGINX cached that response.

When the next request arrived with an explicit Spanish preference, the cache returned the previously stored redirect without considering the application state represented by the language cookie.

WordPress never had an opportunity to evaluate the preference.

09 · APPLICATION VS. CACHE

More PHP Could Not Fix a Request That Never Reached PHP

The explicit language branches were already returning restrictive cache headers.

Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private

Those headers were correct.

But they only existed after WordPress executed the routing logic.

Once NGINX already had a cached representation of the root URL, it could answer the next request before PHP, WordPress or RG Language Bridge executed.

Visitor
NGINX Cache
PHP
WordPress
RG Language Bridge

In the failing request, execution effectively stopped at the second node.

Visitor ↓ NGINX CACHE ↓ cached 301 → /en/ ↓ STOP PHP → not executed WordPress → not executed RG Language Bridge → not executed

Even sending explicit client headers such as:

Cache-Control: no-cache Pragma: no-cache

did not change the cached response.

Application code cannot correct a decision made before the application executes.

10 · PROVING THE ROOT CAUSE

Remove One Variable and Repeat the Same Test

To confirm the diagnosis, we disabled NGINX caching and repeated exactly the same sequence.

NGINX CACHE DISABLED
Cookie ES
302 → /es/ ✓
No cookie
301 → /en/ ✓
Cookie ES again
302 → /es/ ✓

Nothing else changed.

Same WordPress installation.

Same RG Language Bridge release.

Same routing contract.

Same cookies.

Same URLs.

The only variable removed was NGINX caching.

That isolated the failure to the infrastructure layer.

11 · SYSTEM INTERACTION

Every Component Was Correct in Isolation

This is where debugging stops being a component problem and becomes an architecture problem.

RG Language Bridge correctly evaluated the language preference.

WordPress correctly executed the application logic.

NGINX correctly cached repeated responses.

The failure appeared because the cache treated two requests as equivalent even though the application intentionally treated them as different states.

REQUEST A GET / No language preference REQUEST B GET / Cookie: rg_lang_pref=es CACHE VIEW → same URL APPLICATION VIEW → different state → different response

A cache cannot safely reuse a response when the application response depends on state the cache does not understand.

12 · THE CORRECT INFRASTRUCTURE FIX

Apply Cache According to the Responsibility of Each Route

From an architectural perspective, the ideal solution was not to remove caching broadly across the site.

The language router and the content URLs have different responsibilities and, in an environment with sufficient configuration granularity, can use different cache policies.

PREFERRED

Bypass Cache When the Language Cookie Exists

When the root URL receives a request containing rg_lang_pref, NGINX cache can be bypassed so RG Language Bridge can evaluate the visitor's explicit language preference.

ALTERNATIVE

Do Not Cache the Root Language Router

If cookie-aware bypass rules are unavailable, another option is to exclude only the root URL from server cache while keeping the English and Spanish content URLs cacheable.

IDEAL ARCHITECTURE CONTENT URLS /en/* /es/* → cache normally LANGUAGE ROUTER / → evaluate independently

This was the preferred architecture: allow content to continue benefiting from server caching while preserving access to the state required by the router to make the correct decision.

The final implementation, however, had to adapt to the actual capabilities of the hosting environment.

13 · PRODUCTION RESOLUTION

Adapting the Solution to the Constraints of the Environment

Our preferred solution was to bypass NGINX cache only when the root router needed to evaluate a request containing the rg_lang_pref cookie.

The hosting environment did not allow that level of granularity directly within the shared NGINX configuration for rgdigital.mx without affecting the broader hosting environment.

The available production solution was therefore broader.

Through the .htaccess configuration specific to RG Digital, HTTP headers were added instructing the server cache not to store or reuse responses generated by the site.

Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0 Pragma: no-cache Expires: 0

This kept the change scoped to rgdigital.mx without altering the global configuration of the hosting environment.

The tradeoff is explicit: RG Digital's HTML pages stopped benefiting from NGINX server-side caching.

Under the current hosting constraints, we prioritized correct routing and a consistent multilingual experience over preserving a cache optimization that could not safely distinguish between the different application states.

The application logic did not need to change. The cache policy around the application did.

Verifying the Resolution

After the infrastructure change, we repeated the exact same request sequence that had previously reproduced the collision.

01 · No language preference / → 301 → /en/ ✓ Correct 02 · Spanish preference rg_lang_pref=es / → 302 → /es/ ✓ Correct 03 · No language preference again / → 301 → /en/ ✓ Correct 04 · Spanish preference again rg_lang_pref=es / → 302 → /es/ ✓ Correct 05 · English preference rg_lang_pref=en / → 302 → /en/ ✓ Correct

We also verified the responses returned by the main English and Spanish content routes.

/en/ → 200 OK Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0 /es/ → 200 OK Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0

This confirmed both the resolution of the cache collision and the actual scope of the tradeoff: the site's HTML no longer uses NGINX server-side caching under the current configuration.

The previously reproducible failure disappeared without modifying RG Language Bridge, its routing contract or the multilingual content architecture.

If the hosting environment later supports more granular rules, the architecture can evolve toward a selective bypass for the router while keeping content pages cacheable.

The final test closed the investigation: the application was correct, and the production solution belonged at the boundary between application state and infrastructure caching.

14 · THE ARCHITECTURAL LESSON

A Digital System Does Not End at the CMS

The original symptom looked like a WordPress problem.

Then it looked like a redirect problem.

Then it looked like a cookie problem.

Our own application layer was audited before responsibility was assigned elsewhere.

The actual failure existed in the interaction between the application and the infrastructure serving it.

DNS / Edge
Reverse Proxy
Server Cache
Application
User Experience

Software Engineering gave us control over the application.

Technical QA allowed us to validate that application against its contract.

Infrastructure analysis revealed why a correct application was producing an incorrect experience in production.

Digital Architecture means understanding not only how each component works, but how those components behave when they interact.

DIGITAL ARCHITECTURE AUDIT

Is the problem really where you think it is?

Caching conflicts, redirects, analytics discrepancies, disconnected applications and infrastructure issues often appear to be isolated technical problems. Frequently, they are symptoms of how the underlying digital architecture has been designed.

Request a Digital Architecture Audit →