Actualizas ACF, pruebas un formulario frontend y todo parece funcionar. El contenido se crea, la redirección se ejecuta y no aparece ningún error. Sin embargo, algunos campos añadidos manualmente dejan de guardarse.
Si utilizas acf_form() y construyes parte del formulario con HTML personalizado, este comportamiento tiene una explicación. En este artículo veremos por qué ocurre, qué cambió en ACF y qué soluciones existen según cómo esté construido tu formulario.
Este artículo se centra en un cambio concreto relacionado con el guardado de campos. Si quieres aprender a crear formularios frontend con Advanced Custom Fields desde cero o entender mejor cómo funciona acf_form(), puedes consultar primero nuestra guía sobre formularios frontend con ACF.
Qué ha cambiado en acf_form()
El origen de este comportamiento está en un cambio introducido en Advanced Custom Fields 6.8.2, publicado el 26 de mayo de 2026. A partir de esta versión, acf_form() ya no procesa cualquier valor recibido dentro de $_POST['acf'], sino únicamente aquellos campos que forman parte del formulario.
En la práctica, esto significa que ACF solo guarda los campos que cumplen alguna de estas condiciones:
- Están incluidos en el parámetro
fields. - Pertenecen a alguno de los
field_groupsasignados al formulario. - Se incluyen mediante las reglas de ubicación del grupo de campos correspondiente.
Cualquier otro valor enviado con un nombre como:
$_POST['acf']['field_123456789abcd']puede descartarse durante el proceso de guardado, aunque el formulario se haya enviado correctamente.
Antes de este cambio era relativamente habitual aprovechar un comportamiento más permisivo de acf_form(). Muchos desarrolladores renderizaban parte del formulario manualmente y utilizaban nombres con el formato acf[field_xxxxx], confiando en que ACF procesaría esos valores igual que si hubiera generado el campo de forma nativa.
Ese comportamiento ya no debe darse por hecho. El objetivo de este cambio es reforzar la seguridad de los formularios frontend, evitando que puedan modificarse campos que realmente no forman parte del formulario definido por ACF.
Desde el punto de vista de la seguridad, la decisión tiene todo el sentido. Sin embargo, también implica que formularios que llevaban años funcionando pueden dejar de guardar algunos valores sin mostrar ningún error visible, lo que hace que el problema resulte especialmente difícil de detectar si no conoces este cambio.
Qué formularios pueden verse afectados
No todos los formularios creados con acf_form() se ven afectados por este cambio. Si dejas que ACF renderice todos los campos y no modificas su estructura, lo más probable es que todo siga funcionando exactamente igual.
El problema suele aparecer cuando el formulario combina campos generados por ACF con HTML personalizado. Es un enfoque bastante habitual cuando necesitas crear una interfaz más flexible o adaptar la experiencia de usuario a las necesidades del proyecto.
Un caso muy común sería el siguiente:
- Renderizas un formulario con
acf_form(). - Limitas los campos visibles mediante el parámetro
fields. - Añades otros controles utilizando
html_before_fields,html_after_fieldso directamente desde una plantilla personalizada. - Esos controles utilizan nombres como
acf[field_xxxxxxxxxxxxx]para aprovechar el sistema de guardado de ACF.
Por ejemplo:
acf_form(
array(
'post_id' => 'new_post',
'post_title' => false,
'post_content' => false,
'fields' => array(
'field_nombre',
'field_email',
'field_mensaje',
),
'html_after_fields' => $html_checkboxes_restaurantes,
)
);Supongamos que el contenido de $html_checkboxes_restaurantes genera un campo como este:
<input type="checkbox" name="acf[field_restaurantes][]" value="Pepiño">Hasta ahora podía parecer suficiente para que ACF guardara automáticamente el valor enviado. Sin embargo, si field_restaurantes no forma parte del formulario según la configuración de ACF, ese campo puede dejar de procesarse durante el guardado.
Es importante entender que el problema no está en el HTML ni en el tipo de campo que estés utilizando. Puedes generar checkboxes, selects, campos ocultos o cualquier otro control válido. Lo que determina si ACF guardará ese valor es si reconoce esa field key como parte del formulario.
Por eso, este cambio suele afectar especialmente a proyectos que llevan tiempo en producción y que, por necesidades de diseño o de lógica de negocio, renderizan algunos campos manualmente mientras delegan el resto del formulario en acf_form(). Es una técnica completamente válida, pero ahora requiere indicar explícitamente a ACF qué campos debe aceptar durante el proceso de guardado.
Cómo comprobar si este cambio te está afectando
Si después de actualizar ACF algunos campos dejan de guardarse sin mostrar ningún error, merece la pena revisar cómo está construido el formulario. En la mayoría de los casos, el problema aparece cuando acf_form() solo renderiza parte de los campos y el resto se generan manualmente.
Una forma rápida de identificar si tu proyecto puede verse afectado es comprobar si cumple alguna de estas condiciones:
- Utiliza
acf_form()para crear formularios frontend. - Limita los campos mediante el parámetro
fields. - Añade HTML personalizado con
html_before_fields,html_after_fields, hooks o plantillas propias. - Genera manualmente campos con nombres como
acf[field_xxxxx]. - Depende de campos que no aparecen en
fieldsni pertenecen a unfield_groupasociado al formulario.
También puede ser útil buscar en el proyecto algunos patrones de código como estos:
acf_form(
'fields'
'field_groups'
'html_before_fields'
'html_after_fields'
name="acf[
update_field(
....Si encuentras alguno de ellos, el siguiente paso es probar el flujo completo del formulario:
- Crea o edita un contenido desde el formulario frontend.
- Rellena también los campos que se generan manualmente.
- Envía el formulario.
- Comprueba desde el administrador si esos valores se han guardado.
- Revisa además cualquier funcionalidad que dependa de esos campos, como filtros, columnas personalizadas, envíos de correo o automatizaciones.
Si el contenido principal se guarda correctamente, pero solo fallan los campos añadidos manualmente, es muy probable que el origen del problema sea este cambio en el funcionamiento de acf_form().
Antes de modificar el código, también conviene revisar la consola del navegador y el registro de errores de PHP. Aunque este comportamiento normalmente no genera errores visibles, hacerlo te permitirá descartar problemas de JavaScript, conflictos con otros plugins o errores del servidor que puedan estar provocando un síntoma parecido.
En Bubuku solemos empezar siempre por reproducir el problema antes de aplicar ninguna solución. En este tipo de incidencias es fácil asumir que el fallo está en el código que genera el HTML, cuando en realidad el formulario se está enviando correctamente y es ACF quien decide ignorar determinados campos durante el guardado.
Solución 1: incluir el campo en fields o field_groups
Siempre que sea posible, esta es la solución más sencilla y recomendable. Si el campo forma parte del formulario, lo ideal es que ACF también lo considere parte de su configuración, en lugar de confiar únicamente en el HTML generado manualmente.
La forma más directa de conseguirlo es añadir la field key al parámetro fields:
acf_form(
array(
'post_id' => 'new_post',
'post_title' => false,
'post_content' => false,
'fields' => array(
'field_nombre',
'field_email',
'field_mensaje',
'field_restaurantes',
),
)
);Otra posibilidad consiste en renderizar directamente el grupo de campos completo utilizando field_groups:
acf_form(
array(
'post_id' => 'new_post',
'post_title' => false,
'post_content' => false,
'field_groups' => array(
Pepiño,
Manolito
),
)
);Con cualquiera de estas opciones, el comportamiento vuelve a ser el esperado porque ACF conoce de antemano qué campos forman parte del formulario. Esto le permite renderizarlos, validarlos y guardarlos utilizando su flujo habitual, sin necesidad de realizar ninguna lógica adicional.
Sin embargo, esta solución no siempre encaja en todos los proyectos. En muchos desarrollos el HTML se genera manualmente porque el campo necesita una interfaz completamente personalizada: listas filtradas según el usuario conectado, agrupaciones especiales, buscadores dinámicos o componentes que no coinciden con la representación estándar de ACF.
En esos casos, incluir el campo en fields únicamente para que ACF lo procese puede generar un efecto secundario poco deseable: el mismo campo termina renderizándose dos veces, una por ACF y otra por tu propio código. Aunque ocultar el campo nativo con CSS puede servir como solución rápida, no suele ser la alternativa más limpia ni la más fácil de mantener.
En proyectos como los que desarrollamos en Bubuku, intentamos que cada campo tenga un único responsable de renderizar la interfaz. Si el HTML necesita ser completamente personalizado, preferimos que ACF se encargue únicamente de la validación y el guardado, dejando toda la presentación en manos del código del proyecto. Ese enfoque suele simplificar el mantenimiento y evita comportamientos difíciles de depurar cuando el formulario evoluciona.
Solución 2: usar acf/form/allowed_field_keys
Cuando el campo ya existe en ACF, pero necesitas renderizar su HTML manualmente, la solución más mantenible es utilizar el filtro acf/form/allowed_field_keys.
Este filtro, incorporado junto con los cambios de seguridad en los formularios frontend, permite ampliar la lista de field keys que ACF considera válidas durante el guardado. En otras palabras, puedes seguir construyendo una interfaz completamente personalizada sin renunciar al sistema de validación y guardado del propio plugin.
Esta diferencia es importante porque no estás sustituyendo el comportamiento de ACF, sino ampliándolo de forma controlada. ACF sigue siendo el encargado de validar el campo, aplicar sus filtros y almacenarlo igual que cualquier otro campo del formulario.
add_filter( 'acf/form/allowed_field_keys', 'prefix_form_allow_restaurant_key', 10, 2 );
/**
* Permite guardar el campo de restaurantes cuando se renderiza manualmente
* dentro de determinados formularios frontend.
*
* @param array $keys Keys ACF permitidas en $_POST['acf'].
* @param array $form Configuración validada del formulario.
* @return array
*/
function prefix_form_allow_restaurant_key( $keys, $form ) {
if ( is_admin() ) {
return $keys;
}
$restaurant_field_key = 'field_restaurantes';
$allowed_post_types = array(
'post',
'order_custom'
);
$post_type = '';
if ( isset( $form['post_id'] ) && 'new_post' === $form['post_id'] ) {
$post_type = $form['new_post']['post_type'] ?? '';
} elseif ( ! empty( $form['post_id'] ) && is_numeric( $form['post_id'] ) ) {
$post_type = get_post_type( absint( $form['post_id'] ) );
}
if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
return $keys;
}
$keys[] = $restaurant_field_key;
return array_values( array_unique( $keys ) );
}En este ejemplo, field_restaurantes se añade a la lista de campos que ACF acepta durante el guardado, pero únicamente para los tipos de contenido esperados. En un proyecto real deberás sustituir este valor por la field key correspondiente, normalmente con un formato similar a field_abc123....
La ventaja de este enfoque es que el formulario continúa utilizando el flujo normal de ACF. No necesitas llamar a update_field(), ni volver a implementar validaciones, ni preocuparte de mantener dos procesos de guardado diferentes.
Esta solución resulta especialmente recomendable cuando:
- El campo existe en ACF.
- Necesitas personalizar completamente la interfaz del formulario.
- El HTML utiliza correctamente nombres como
acf[field_xxxxx]. - Quieres que ACF siga encargándose de validar y guardar el valor.
En Bubuku solemos optar por este enfoque cuando el formulario requiere una interfaz completamente personalizada. Mantener la lógica de negocio dentro de ACF y limitar el código propio al renderizado del HTML hace que el proyecto sea más fácil de mantener, más escalable y mucho más resistente a futuras actualizaciones. Cuanto menos repliquemos el funcionamiento interno del plugin, menos trabajo tendremos cuando ACF evolucione.
Solución 3: guardar el campo manualmente con acf/save_post
Aunque acf/save_post sigue siendo una herramienta muy útil, ya no debería ser la primera opción si el único objetivo es conseguir que ACF vuelva a guardar un campo existente.
En ese escenario, acf/form/allowed_field_keys ofrece una solución más limpia porque permite que ACF continúe gestionando la validación y el guardado del campo como parte de su flujo habitual.
Sin embargo, acf/save_post sigue teniendo mucho sentido cuando necesitas ir un paso más allá. Por ejemplo, si el valor recibido no corresponde directamente a un campo ACF, necesitas transformar los datos antes de guardarlos o quieres almacenar la información en otro lugar distinto del campo original.
En estos casos, el flujo habitual consiste en:
- Dejar que ACF guarde sus propios campos.
- Ejecutar una acción sobre
acf/save_postcon una prioridad posterior. - Leer el valor recibido desde el formulario.
- Validarlo y sanearlo.
- Guardarlo utilizando
update_field()u otra función adecuada.
Por ejemplo:
add_action( 'acf/save_post', 'prefix_save_manual_restaurants_field', 20 );
/**
* Guarda manualmente un campo renderizado fuera de acf_form().
*
* @param int|string $post_id ID del contenido guardado.
* @return void
*/
function prefix_save_manual_restaurants_field( $post_id ) {
if ( empty( $_POST['acf']['field_restaurantes'] ) ) {
return;
}
$restaurants = array_map(
'absint',
(array) $_POST['acf']['field_restaurantes']
);
update_field(
'field_restaurantes',
$restaurants,
$post_id
);
}Nuestra recomendación es reservar
acf/save_postpara cuando realmente aporta valor. Si el campo ya existe en ACF y solo necesitas renderizar una interfaz personalizada, dejar que el propio plugin siga gestionando la validación y el guardado suele ser una solución más sencilla, escalable y fácil de mantener.
¿Qué solución conviene en cada caso?
Aunque las tres soluciones resuelven el mismo síntoma, no todas responden al mismo problema. Elegir una u otra dependerá de cómo esté construido tu formulario y del grado de personalización que necesites.
| Situación | Solución recomendada |
|---|---|
| El campo puede formar parte del formulario sin modificaciones. | Añadirlo a fields o field_groups. |
| El campo existe en ACF, pero necesitas renderizar una interfaz completamente personalizada. | Utilizar acf/form/allowed_field_keys. |
| Necesitas transformar los datos antes de guardarlos o almacenarlos en otro lugar. | Gestionar el guardado mediante acf/save_post. |
Como ocurre con muchas actualizaciones relacionadas con la seguridad, el objetivo de ACF no es romper la compatibilidad, sino evitar que un formulario frontend pueda modificar campos que no le corresponden. El cambio puede sorprender la primera vez que te encuentras con él, pero una vez entiendes cómo determina ACF qué campos pertenecen al formulario, resulta mucho más sencillo elegir la solución adecuada.
Al final, la idea es trabajar con el flujo de ACF en lugar de reemplazarlo. Cuanto menos código tengas que escribir para replicar su comportamiento interno, más sencillo será mantener el proyecto y adaptarlo a futuras actualizaciones del plugin.
Si tu formulario dejó de guardar algunos campos tras actualizar ACF, antes de reescribir la lógica de guardado merece la pena revisar si acf/form/allowed_field_keys resuelve el problema. En muchos casos, es la forma más sencilla de mantener una interfaz personalizada sin renunciar al flujo nativo de ACF.
Referencias y recursos para profundizar
Si quieres conocer con más detalle el cambio introducido en los formularios frontend de ACF o consultar la documentación oficial sobre el filtro utilizado en este artículo, estos recursos son un buen punto de partida:
- ACF 6.8.2 Security Release
- Advanced Custom Fields Changelog, donde se describen los cambios relacionados con la seguridad de
acf_form(). - Documentación de
acf_form()
Si necesitas desarrollar formularios frontend personalizados con Advanced Custom Fields o adaptar proyectos WordPress a cambios como este, en Bubuku podemos ayudarte a implementar soluciones robustas, escalables y fáciles de mantener.



