---
title: "Why duplicating a page breaks your Elementor layout (and your ACF fields)"
description: "A duplicated page that looks fine but renders wrong is almost always one bug: WordPress strips a level of backslashes on every copy. Here is the mechanism, and how to test your own site in a minute."
pubDate: 2026-08-21
source: https://adminkeep.com/blog/duplicate-post-breaks-elementor-acf/
---

You duplicate a page. The copy appears, the title is right, the content is there. You open it in
the builder and the layout is wrong — a section collapsed, or the whole canvas blank. Or the page
is fine but an ACF repeater came back empty. Or a code block renders `\n` as two literal
characters where the original showed a line break.

These look like four different bugs. They are one, and it is not in your builder.

## Every copy eats one level of backslashes

WordPress has an old and genuinely confusing convention. `wp_insert_post()` and `add_post_meta()`
both expect **slashed** input, and they call `wp_unslash()` on whatever you give them. Values you
read back *out* of the database are already unslashed.

So the obvious implementation of a duplicate is wrong in a silent way:

```php
// Wrong. Loses one level of backslashes on every field, every time.
$value = get_post_meta( $source_id, $key, true );
update_post_meta( $copy_id, $key, $value );
```

The read gives you an unslashed string. The write unslashes it *again*. One level of escaping
disappears, with no error and no warning.

On prose this is invisible — nobody has a backslash in a paragraph. It becomes visible the moment
a field holds structured data, where backslashes are not decoration but syntax:

- **`_elementor_data`** is JSON, which leans on backslashes everywhere: escaped quotes, escaped
  newlines, the escaped forward slashes `json_encode()` puts in every URL. Strip one level and you
  do not get slightly wrong JSON — you get JSON that fails to parse, which is why the canvas comes
  back empty rather than misaligned.
- **ACF repeaters and flexible content** store serialized arrays whose declared string lengths must
  match their contents. Remove a character and `unserialize()` returns `false`. The field renders
  as nothing.
- **Code blocks and regex fields** hold backslashes as literal content. `\d` becomes `d`.

The fix is one function call on every write:

```php
add_post_meta( $to_id, $meta_key, wp_slash( maybe_unserialize( $meta_value ) ) );
```

`wp_slash()` adds the level that `add_post_meta()` is about to remove, so the round trip lands on
the identical stored string. Same rule for `post_content` and `post_content_filtered`.

**It compounds.** Duplicate a page, then duplicate the copy, and you have lost two levels. Teams
that build each new landing page from last month's landing page are running this loop
indefinitely. The first few generations look fine. Somewhere around the third, a page stops
rendering — and nothing points at the duplicate button.

## Test your own site in a minute

Put a string with a real backslash in a custom field — `a\b` is enough — duplicate the post, and
check the copy's value. If it comes back `ab`, your duplicate tool has this bug, and every
structured field on every copy it has ever made is affected.

Adminkeep's Duplicate feature is built around this rule — every write passes through
`wp_slash()`, and meta is read raw so multi-value keys survive in order. No dashboard ads, no
upgrade nags; the absence of them is enforced by a unit test rather than promised on a landing
page.

If you are still deciding whether you need a plugin at all, the
[guide to duplicating a page in WordPress](/guides/duplicate-page-wordpress/) shows the manual
method and exactly what it leaves behind.