DesenvolvedorWordPress WhatsApp

Meta boxes e custom fields no WordPress (post meta)

Custom fields são como o WordPress guarda informações extras de um conteúdo — um preço, uma data, um destaque. Por trás deles está o post meta, e na tela de edição estão as meta boxes. Dominar os dois, com o salvamento seguro, é fundamental para estender o WordPress sem inventar tabelas.

Precisa de campos customizados sob medida?

Atualizado em 19/06/2026

Quase todo conteúdo no WordPress precisa, cedo ou tarde, de informações além do título e do corpo: um preço, uma data de evento, um link de afiliado, um campo "destaque na home". A forma idiomática de armazenar isso é o post meta — pares chave/valor associados a um post, guardados na tabela wp_postmeta. Sobre esse armazenamento, você constrói uma interface com meta boxes (no editor clássico) ou expõe o campo ao editor de blocos via REST. Este artigo cobre a API completa de metadados, a construção de uma meta box e — o ponto inegociável — o salvamento seguro. Como tudo aqui se apoia em hooks, ter esse conceito firme ajuda.

A API de post meta

Quatro funções leem e gravam metadados, e uma quinta os registra formalmente. Elas operam sobre a mesma tabela, e a diferença mais importante entre elas é o conceito de valor único (single) versus múltiplo:

FunçãoUso
get_post_meta( $id, $key, $single )Lê o valor. Com $single = true devolve o valor direto; com false, um array de todos os valores da chave.
update_post_meta( $id, $key, $valor )Grava: atualiza a chave se existir, cria se não existir. É o que você usa em 90% dos casos.
add_post_meta( $id, $key, $valor, $unico )Adiciona um valor. Com $unico = true não cria duplicata; com false, permite múltiplos valores na mesma chave.
delete_post_meta( $id, $key, $valor )Remove a chave. Se informar $valor, apaga só a entrada que casa com ele (útil quando há múltiplos).
register_post_meta( $type, $key, $args )Registra o meta formalmente: define tipo, single, sanitização, permissão e exposição na REST/editor de blocos.

Nuance de update_post_meta: o quarto parâmetro ($prev_value) permite atualizar apenas a entrada cujo valor antigo casa — relevante quando a chave tem múltiplos valores. Sem ele, em uma chave de valor único, a função simplesmente substitui. Para a esmagadora maioria dos campos (valor único), update_post_meta( $id, $key, $valor ) é tudo de que você precisa.

Registrando o meta com register_post_meta()

Antes de criar a interface, registre o campo. Isso não é obrigatório para o post meta funcionar, mas é a boa prática moderna: você declara o tipo, se é single, uma sanitização centralizada, um controle de permissão e — crucialmente — se ele aparece na REST API (e portanto no editor de blocos):

<?php
add_action( 'init', 'meusite_registra_meta' );

function meusite_registra_meta() {
    register_post_meta( 'post', '_meusite_subtitulo', array(
        'type'              => 'string',
        'single'            => true,
        'show_in_rest'      => true,                       // expõe ao editor de blocos / REST
        'sanitize_callback' => 'sanitize_text_field',      // saneia ANTES de gravar
        'auth_callback'     => function () {
            return current_user_can( 'edit_posts' );       // quem pode escrever via REST
        },
    ) );
}

O sanitize_callback roda automaticamente sempre que o meta é gravado pela REST, o que reduz a chance de salvar dados sujos. O auth_callback controla quem pode editar o meta via REST — não substitui a checagem de capability no salvamento clássico, que veremos adiante. E o show_in_rest é a chave que liga o campo ao Gutenberg.

Criando uma meta box no editor clássico

Uma meta box é uma caixa na tela de edição do post. Você a registra no hook add_meta_boxes com add_meta_box(), cuja assinatura completa é add_meta_box( $id, $titulo, $callback, $screen, $context, $priority ). O $callback é quem renderiza os campos — e é ali que você imprime um wp_nonce_field(), peça central da segurança no salvamento:

<?php
add_action( 'add_meta_boxes', 'meusite_add_meta_box' );

function meusite_add_meta_box() {
    add_meta_box(
        'meusite_subtitulo_box',          // id (HTML)
        'Subtítulo do post',              // título visível
        'meusite_render_meta_box',        // callback que desenha os campos
        'post',                           // tela (post type) onde aparece
        'side',                           // contexto: 'normal' | 'side' | 'advanced'
        'default'                         // prioridade
    );
}

// Callback: renderiza o campo + o nonce.
function meusite_render_meta_box( $post ) {
    $valor = get_post_meta( $post->ID, '_meusite_subtitulo', true );

    // Nonce: protege o salvamento contra CSRF.
    wp_nonce_field( 'meusite_salvar_subtitulo', 'meusite_subtitulo_nonce' );
    ?>
    <label for="meusite_subtitulo">Subtítulo</label>
    <input type="text" id="meusite_subtitulo" name="meusite_subtitulo"
           value="<?php echo esc_attr( $valor ); ?>" class="widefat">
    <?php
}

Sempre escape na saída. Note o esc_attr() no value do input: o valor salvo é dado não confiável e precisa ser escapado ao ser impresso em um atributo HTML, exatamente como em qualquer outro lugar — o tema da segurança no código. Saneie na entrada, escape na saída.

O salvamento seguro: a tríade obrigatória

Aqui está o coração do assunto. O hook save_post dispara em situações que você precisa filtrar: ele roda para todos os post types, durante o autosave e ao salvar revisões. Ignorar isso causa desde sobrescrever dados com valores vazios até brechas de segurança. Antes de gravar qualquer coisa, passe pelas três barreiras — nonce, autosave/revisão e capability — e só então sanitize e grave:

<?php
add_action( 'save_post', 'meusite_salvar_meta' );

function meusite_salvar_meta( $post_id ) {

    // (1) NONCE: o formulário veio mesmo da nossa meta box?
    if ( ! isset( $_POST['meusite_subtitulo_nonce'] )
        || ! wp_verify_nonce( $_POST['meusite_subtitulo_nonce'], 'meusite_salvar_subtitulo' ) ) {
        return;
    }

    // (2) AUTOSAVE / REVISÃO: ignore salvamentos automáticos e revisões.
    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        || wp_is_post_autosave( $post_id )
        || wp_is_post_revision( $post_id ) ) {
        return;
    }

    // (3) CAPABILITY: este usuário pode editar ESTE post?
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    // Só agora: SANITIZE na entrada e grave.
    if ( isset( $_POST['meusite_subtitulo'] ) ) {
        $valor = sanitize_text_field( wp_unslash( $_POST['meusite_subtitulo'] ) );
        update_post_meta( $post_id, '_meusite_subtitulo', $valor );
    } else {
        delete_post_meta( $post_id, '_meusite_subtitulo' );
    }
}

Por que cada barreira importa: sem o nonce, um site malicioso poderia forjar a requisição (CSRF). Sem ignorar o autosave, o salvamento automático do editor (que não envia os seus campos) sobrescreveria o meta com vazio. Sem a capability, um usuário com acesso ao painel mas sem permissão sobre aquele post conseguiria alterá-lo. Note também o wp_unslash() antes de sanitizar: o WordPress adiciona barras a $_POST, e você precisa removê-las antes de gravar.

Lendo o valor no template

Para exibir o campo no front-end, leia com get_post_meta() usando $single = true e escape conforme o contexto de saída:

<?php
$subtitulo = get_post_meta( get_the_ID(), '_meusite_subtitulo', true );

if ( $subtitulo ) {
    echo '<p class="subtitulo">' . esc_html( $subtitulo ) . '</p>';
}

Com true, você recebe a string direto. Se tivesse usado false (ou omitido o parâmetro), receberia um array — e o echo falharia. Essa é a origem do clássico "meu campo virou um array". Mantenha a coerência: se o meta é de valor único, sempre leia com true.

Convenções de nomenclatura das meta keys

Dois hábitos evitam dor de cabeça em produção:

Expondo ao editor de blocos (Gutenberg)

No editor de blocos, a meta box clássica continua funcionando, mas a abordagem nativa é outra: você registra o meta com show_in_rest => true (como fizemos acima) e o edita por JavaScript, lendo e gravando o valor via useEntityProp e exibindo um controle em um painel lateral (por exemplo, um PluginDocumentSettingPanel). O ponto a guardar é o pré-requisito do lado do servidor: sem show_in_rest, o editor de blocos não enxerga o meta, por mais que ele exista no banco. O registro com sanitização e auth_callback também garante que a gravação via REST seja saneada e autorizada.

Armadilhas comuns

Boas práticas

Precisa de campos customizados e meta boxes feitos com segurança?

Crio meta boxes, campos no editor de blocos e o salvamento seguro (nonce, capability e sanitização) seguindo os padrões do WordPress. Me chame no WhatsApp.

WhatsApp: (43) 99932-9697

ou e-mail: [email protected]

Perguntas frequentes

Qual a diferença entre custom fields, post meta e meta boxes?

São camadas distintas. Post meta é o armazenamento: pares chave/valor ligados a um post, guardados na tabela wp_postmeta. Custom fields é o nome genérico desses metadados e também o nome do painel nativo, simples, que o WordPress mostra para editá-los. Meta boxes são caixas customizadas na tela de edição que você cria para oferecer uma interface amigável (campos próprios) sobre esse mesmo post meta — em vez do painel cru de chave/valor.

Por que single = true muda o que get_post_meta() retorna?

Porque o post meta suporta múltiplos valores para a mesma chave. Com $single = true, get_post_meta() retorna o valor diretamente (string, número, array salvo). Com false (o padrão), retorna um array de todos os valores daquela chave. Confundir os dois é causa frequente de "meu campo virou um array do nada". Defina o comportamento no register_post_meta() com a chave single e seja consistente.

Para que serve o prefixo "_" na meta key?

Uma meta key iniciada com underscore (ex.: _meu_preco) é considerada protegida: o WordPress não a exibe no painel nativo de Custom Fields da tela de edição. Use o prefixo _ para metadados internos, gerenciados pelo seu código/meta box, que o usuário não deve editar manualmente pelo painel cru. Mantenha também um prefixo do seu projeto (ex.: _meusite_) para evitar colisão com outros plugins.

Preciso mesmo verificar nonce, autosave e capability ao salvar?

Sim, os três, sempre. O hook save_post dispara para todos os post types, em revisões e em autosaves. Sem checar o nonce, você abre brecha de CSRF; sem ignorar o autosave, sobrescreve dados com valores vazios; sem checar a capability, permite que usuários sem permissão alterem o campo. Só depois dessas três barreiras você sanitiza e grava. É a tríade obrigatória do salvamento seguro.

Como deixo o custom field editável no editor de blocos (Gutenberg)?

Registrando o meta com register_post_meta() e 'show_in_rest' => true. Isso expõe o campo à REST API e, com isso, ao editor de blocos — onde um plugin de JavaScript pode lê-lo e gravá-lo via useEntityProp em um painel lateral (PluginDocumentSettingPanel). Sem show_in_rest, o meta existe no banco, mas o editor de blocos não o enxerga.

Custom fields servem para Custom Post Types?

Servem para qualquer post type — posts, páginas e Custom Post Types. O post meta é a forma idiomática de guardar atributos que não justificam uma taxonomia (que serve para classificar e filtrar) nem uma tabela própria. Para dados pontuais de um item (preço, SKU, data do evento), post meta é o caminho certo.

Referências oficiais

  1. WordPress.org — Metadata (Plugin Handbook)
  2. WordPress.org — Custom Meta Boxes (Plugin Handbook)
  3. WordPress.org — add_meta_box() (Code Reference)
  4. WordPress.org — register_post_meta() (Code Reference)
Tem alguma dúvida? Ficarei feliz em ajudar. Clique Aqui