【WordPress】カスタムフィールドのURLを取得してリンクを貼る方法|get_post_meta・セキュリティ・実務パターン完全ガイド

【WordPress】カスタムフィールドのURLを取得してリンクを貼る方法|get_post_meta・セキュリティ・実務パターン完全ガイド WordPress

WordPressのカスタムフィールドにURLを入力し、テンプレートでリンクとして表示したい場面は多くあります。参考サイトのリンク、外部サービスへの誘導、ダウンロードURL、SNSプロフィールなど、用途はさまざまです。

この記事では、get_post_meta()によるURL取得の基本から、セキュリティ対策(サニタイズ・バリデーション・エスケープの3層防御)、カスタムメタボックスの作成、ACF対応ショートコード化WP_Queryによるフィルタリングまで、実務で必要な知識を体系的に解説します。

この記事で学べること

  • get_post_meta() でカスタムフィールドのURLを取得する基本
  • esc_url() / sanitize_url() / FILTER_VALIDATE_URL によるセキュリティ3層防御
  • カスタムメタボックスでURL入力欄を作成する方法
  • ACF(URL・リンクフィールド)での実装方法
  • ショートコード化・WP_Queryフィルタリング・REST API連携
  • 7つの実務パターン(参考リンク・SNS・ダウンロード等)
スポンサーリンク
  1. 基本:get_post_meta() でカスタムフィールドのURLを取得する
    1. カスタムフィールド取得の代替関数
  2. リンクテキストもカスタムフィールドで管理する
  3. セキュリティ:URL処理の3層防御
    1. URL関連関数の使い分け
    2. URLの正規化(保存前の前処理)
  4. カスタムメタボックスでURL入力欄を作成する
    1. アンダースコアプレフィックスで管理画面から隠す
  5. ACF(Advanced Custom Fields)での実装
    1. URLフィールド vs リンクフィールドの違い
    2. ACF URLフィールドの出力
    3. ACF リンクフィールドの出力
  6. 複数URLを管理する(参考リンク一覧)
    1. 方法1:同じキーで複数値を登録
    2. 方法2:ACFリピーターフィールド
  7. ショートコードでカスタムフィールドのURLを出力する
  8. WP_Queryでカスタムフィールドの有無によるフィルタリング
    1. 特定ドメインのURLを含む投稿を検索
  9. register_post_meta()でREST API対応にする
  10. パフォーマンス:get_post_meta のキャッシュの仕組み
  11. 実務で使える7つのパターン
    1. パターン1:参考リンクセクション
    2. パターン2:ダウンロードボタン
    3. パターン3:SNSプロフィールリンク(著者情報)
    4. パターン4:外部リンクへのリダイレクト
    5. パターン5:投稿ヘッダーにOGP画像URLを設定
    6. パターン6:条件付き表示(値なしの場合の処理)
    7. パターン7:管理画面の投稿一覧にURL列を追加
  12. トラブルシューティング
  13. まとめ

基本:get_post_meta() でカスタムフィールドのURLを取得する

カスタムフィールドに入力されたURLを取得するには、get_post_meta() 関数を使います。取得した値を esc_url() でエスケープしてからリンクタグに出力するのが基本パターンです。

single.php / page.php
<?php
// カスタムフィールド「custom_url」の値を取得
$custom_url = get_post_meta( get_the_ID(), 'custom_url', true );

// 値が存在する場合のみリンクを表示
if ( $custom_url ) {
    echo '<a href="' . esc_url( $custom_url ) . '">参考サイトはこちら</a>';
}
?>
引数 説明
$post_id 投稿ID get_the_ID()
$key カスタムフィールドのキー名 'custom_url'
$single true = 単一の値、false = 配列で返す true(通常はtrue)

⚠ 注意:第3引数の $single を省略すると配列が返ります。URLを1つだけ取得する場合は必ず true を指定してください。false(デフォルト)の場合、同じキーに複数の値が登録されているケースでは全て配列で返されます。

カスタムフィールド取得の代替関数

関数 返り値 用途
get_post_meta( $id, $key, true ) 単一の値(文字列) 最もよく使う(推奨)
get_post_meta( $id, $key, false ) 配列(同キー全値) 同キーに複数値がある場合
get_post_meta( $id ) 全メタデータの連想配列 デバッグ・一括取得
get_post_custom( $id ) 全メタデータの連想配列 get_post_meta($id) と同等
get_post_custom_values( $key, $id ) 指定キーの値の配列 引数の順序が逆(非推奨)

⚠ 注意:get_post_custom()get_post_custom_values() は内部的に get_post_meta() のラッパーです。特別な理由がない限り get_post_meta() を使いましょう。

関連記事:投稿のカスタムフィールドを取得する方法get_post_meta完全ガイド

リンクテキストもカスタムフィールドで管理する

URLだけでなく、リンクのテキスト(アンカーテキスト)もカスタムフィールドで管理すると柔軟性が増します。

URL + テキストを取得
<?php
$url  = get_post_meta( get_the_ID(), 'ref_url', true );
$text = get_post_meta( get_the_ID(), 'ref_text', true );

if ( $url ) {
    // テキスト未入力時はURLをそのまま表示
    $label = $text ? esc_html( $text ) : esc_url( $url );
    printf(
        '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
        esc_url( $url ),
        $label
    );
}
?>

? ポイント:外部サイトへのリンクには target="_blank"rel="noopener noreferrer" を付けましょう。noopener はセキュリティ対策(window.opener へのアクセス防止)、noreferrer はリファラー送信の抑制です。

セキュリティ:URL処理の3層防御

カスタムフィールドのURLを扱う際は、入力・保存・出力の3段階でセキュリティ対策を行うことが重要です。これを怠ると、XSS(クロスサイトスクリプティング)やフィッシング攻撃の原因になります。

段階 目的 使用する関数 処理内容
① 入力サニタイズ 不正な文字を除去 sanitize_url()
esc_url_raw()
危険な文字・プロトコルを除去してDBに保存
② バリデーション URL形式かチェック filter_var(FILTER_VALIDATE_URL)
wp_http_validate_url()
正しいURL形式でなければ保存しない
③ 出力エスケープ HTML内で安全に出力 esc_url()
esc_attr()
href属性やHTMLコンテキストに応じてエスケープ
3層防御の実装例
// ① 保存時:サニタイズ + バリデーション
function save_custom_url( $post_id ) {
    $raw_url = $_POST['custom_url'] ?? '';

    // サニタイズ:危険なプロトコル(javascript: 等)を除去
    $sanitized = sanitize_url( $raw_url );

    // バリデーション:正しいURL形式かチェック
    if ( $sanitized && filter_var( $sanitized, FILTER_VALIDATE_URL ) ) {
        update_post_meta( $post_id, 'custom_url', $sanitized );
    } else {
        delete_post_meta( $post_id, 'custom_url' );
    }
}

// ③ 出力時:esc_url() でエスケープ
$url = get_post_meta( get_the_ID(), 'custom_url', true );
if ( $url ) {
    echo '<a href="' . esc_url( $url ) . '">リンク</a>';
}

? 危険な例:以下は絶対にNGです。エスケープなしの出力はXSS攻撃の原因になります。

// ❌ 危険:エスケープなし
echo '<a href="' . $custom_url . '">リンク</a>';

// ❌ 危険:javascript: プロトコルが実行される
// 攻撃者が custom_url に「javascript:alert(1)」を入力した場合
echo '<a href="' . $custom_url . '">クリック</a>';

URL関連関数の使い分け

関数 用途 使用場面 javascript: 除去
esc_url() 出力エスケープ href属性、src属性に出力する時
esc_url_raw() DB保存用サニタイズ DBに保存する前(HTMLエンコードなし)
sanitize_url() DB保存用サニタイズ esc_url_raw() のエイリアス(WP 5.9+)
wp_http_validate_url() HTTPリクエスト用バリデーション 外部URLへリクエストを送る前の検証
filter_var(FILTER_VALIDATE_URL) PHP標準バリデーション URL形式の厳密チェック

URLの正規化(保存前の前処理)

ユーザーが入力するURLには揺れがあります。保存前に正規化することで、検索やマッチングの精度が向上します。

URL正規化関数
/**
 * URLを正規化する
 * - スキームなし → https:// を付与
 * - 末尾スラッシュを統一
 * - 不要なクエリパラメータを除去
 */
function normalize_custom_url( $url ) {
    $url = trim( $url );

    // スキームがなければ https:// を付与
    if ( $url && ! preg_match( '#^https?://#i', $url ) ) {
        $url = 'https://' . $url;
    }

    // 末尾スラッシュを統一(パスがある場合は付与)
    $parsed = wp_parse_url( $url );
    if ( isset( $parsed['path'] ) && $parsed['path'] === '%s' ) {
        $url = trailingslashit( $url );
    }

    return sanitize_url( $url );
}

// 使用例
echo normalize_custom_url( 'example.com' );      // https://example.com/
echo normalize_custom_url( 'http://example.com' ); // http://example.com/

カスタムメタボックスでURL入力欄を作成する

WordPressのデフォルトのカスタムフィールド入力欄は使いづらいため、専用のメタボックスを作成すると使い勝手が大幅に向上します。

functions.php(子テーマ)
/**
 * URL入力用カスタムメタボックスを追加
 */
function add_url_meta_box() {
    add_meta_box(
        'custom_url_box',      // メタボックスID
        '参考リンク',           // タイトル
        'render_url_meta_box',  // コールバック
        'post',                 // 投稿タイプ
        'normal',               // 表示位置
        'high'                  // 優先度
    );
}
add_action( 'add_meta_boxes', 'add_url_meta_box' );

/**
 * メタボックスのHTML出力
 */
function render_url_meta_box( $post ) {
    // nonceフィールド(CSRF対策)
    wp_nonce_field( 'save_custom_url', 'custom_url_nonce' );

    $url  = get_post_meta( $post->ID, 'custom_url', true );
    $text = get_post_meta( $post->ID, 'custom_url_text', true );
    ?>
    <table class="form-table">
        <tr>
            <th><label for="custom_url">URL</label></th>
            <td>
                <input type="url" id="custom_url" name="custom_url"
                       value="<?php echo esc_attr( $url ); ?>"
                       class="large-text" placeholder="https://example.com">
            </td>
        </tr>
        <tr>
            <th><label for="custom_url_text">リンクテキスト</label></th>
            <td>
                <input type="text" id="custom_url_text" name="custom_url_text"
                       value="<?php echo esc_attr( $text ); ?>"
                       class="large-text" placeholder="参考サイト名">
            </td>
        </tr>
    </table>
    <?php
}

? type=”url” について:HTML5の <input type="url"> はブラウザ側でURL形式のバリデーションを行います。ただし、チェックされるのはフォーム送信時のみで、JavaScriptで送信した場合やAjax保存時はバイパスされます。そのため、サーバーサイドでのバリデーションは必須です。

functions.php(保存処理)
/**
 * メタボックスの値を保存
 */
function save_url_meta_box( $post_id ) {
    // nonce検証
    if ( ! isset( $_POST['custom_url_nonce'] ) ||
         ! wp_verify_nonce( $_POST['custom_url_nonce'], 'save_custom_url' ) ) {
        return;
    }

    // 自動保存時はスキップ
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }

    // 権限チェック
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    // URL:サニタイズ + バリデーション + 保存
    $url = sanitize_url( $_POST['custom_url'] ?? '' );
    if ( $url && filter_var( $url, FILTER_VALIDATE_URL ) ) {
        update_post_meta( $post_id, 'custom_url', $url );
    } else {
        delete_post_meta( $post_id, 'custom_url' );
    }

    // テキスト:サニタイズ + 保存
    $text = sanitize_text_field( $_POST['custom_url_text'] ?? '' );
    update_post_meta( $post_id, 'custom_url_text', $text );
}
add_action( 'save_post', 'save_url_meta_box' );

? セキュリティのポイント:保存処理では必ず①nonce検証(CSRF対策)、②自動保存チェック、③権限チェックの3つを行いましょう。これはWordPressでメタボックスを作成する際の鉄則です。

アンダースコアプレフィックスで管理画面から隠す

カスタムフィールドのキー名を _(アンダースコア)で始めると、WordPress管理画面の「カスタムフィールド」パネルに表示されなくなります。メタボックスで専用UIを作成した場合、ユーザーが誤って直接編集するのを防げます。

プレフィックス付きキーの例
// ❌ 管理画面のカスタムフィールド欄に表示される
update_post_meta( $post_id, 'custom_url', $url );

// ✅ 管理画面のカスタムフィールド欄に表示されない(推奨)
update_post_meta( $post_id, '_custom_url', $url );

// 取得時も同じプレフィックスを指定
$url = get_post_meta( get_the_ID(), '_custom_url', true );

⚠ 注意:ACFも内部的にアンダースコアプレフィックスを使用しています。ACFフィールドのデータを get_post_meta() で直接取得する場合は、フィールド名の前に _ が付いたメタキーも保存されている点に注意してください(例: reference_url_reference_url にACFの設定値が格納)。

ACF(Advanced Custom Fields)での実装

ACFを使えば、コードを書かずにURL入力フィールドを作成できます。ACFにはURLに関連するフィールドが2種類あります。

URLフィールド vs リンクフィールドの違い

項目 URLフィールド リンクフィールド
返り値 文字列(URLのみ) 配列(url, title, target)
入力UI URLテキストボックス URL + テキスト + target選択
内部リンク検索 ❌ なし ✅ あり(投稿検索ポップアップ)
バリデーション URL形式チェック URL形式チェック
おすすめ用途 外部URLのみ必要な場合 テキスト・target も管理したい場合

ACF URLフィールドの出力

ACF URLフィールド
<?php
$url = get_field( 'reference_url' );

if ( $url ) {
    echo '<a href="' . esc_url( $url ) . '" target="_blank" rel="noopener">参考サイト</a>';
}
?>

ACF リンクフィールドの出力

ACF リンクフィールド(配列で取得)
<?php
$link = get_field( 'reference_link' );

if ( $link ) {
    $url    = esc_url( $link['url'] );
    $title  = esc_html( $link['title'] );
    $target = $link['target'] ? esc_attr( $link['target'] ) : '_self';

    printf(
        '<a href="%s" target="%s" rel="noopener noreferrer">%s</a>',
        $url,
        $target,
        $title
    );
}
?>

関連記事:カスタムフィールドの入力有無に応じて表示を切り替える方法

複数URLを管理する(参考リンク一覧)

1つの投稿に複数の参考URLを登録したいケースは多くあります。ネイティブのカスタムフィールドとACFリピーターフィールドの2つの方法を紹介します。

方法1:同じキーで複数値を登録

同じキーの複数値を全て取得
<?php
// 第3引数を false にすると配列で全件取得
$urls = get_post_meta( get_the_ID(), 'ref_urls', false );

if ( ! empty( $urls ) ) {
    echo '<h3>参考リンク</h3>';
    echo '<ul>';
    foreach ( $urls as $url ) {
        if ( filter_var( $url, FILTER_VALIDATE_URL ) ) {
            printf( '<li><a href="%s" target="_blank" rel="noopener">%s</a></li>',
                esc_url( $url ),
                esc_html( $url )
            );
        }
    }
    echo '</ul>';
}
?>

関連記事:カスタムフィールドの値をリストタグで囲う方法

方法2:ACFリピーターフィールド

ACF リピーターフィールド
<?php
if ( have_rows( 'reference_links' ) ) : ?>
    <div class="reference-links">
        <h3>参考リンク</h3>
        <ul>
        <?php while ( have_rows( 'reference_links' ) ) : the_row(); ?>
            <li>
                <a href="<?php echo esc_url( get_sub_field( 'url' ) ); ?>"
                   target="_blank" rel="noopener noreferrer">
                    <?php echo esc_html( get_sub_field( 'label' ) ); ?>
                </a>
            </li>
        <?php endwhile; ?>
        </ul>
    </div>
<?php endif; ?>

ショートコードでカスタムフィールドのURLを出力する

テンプレートファイルを編集せずに、記事本文内でカスタムフィールドのURLをリンクとして表示したい場合は、ショートコードを作成します。

functions.php(ショートコード登録)
/**
 * [custom_link key="custom_url" text="参考サイト" new_tab="true"]
 */
function custom_link_shortcode( $atts ) {
    $atts = shortcode_atts( array(
        'key'     => 'custom_url',
        'text'    => '',
        'new_tab' => 'true',
        'class'   => '',
    ), $atts, 'custom_link' );

    $url = get_post_meta( get_the_ID(), sanitize_key( $atts['key'] ), true );

    if ( ! $url || ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
        return '';
    }

    $label  = $atts['text'] ? esc_html( $atts['text'] ) : esc_url( $url );
    $target = ( $atts['new_tab'] === 'true' ) ? ' target="_blank" rel="noopener noreferrer"' : '';
    $class  = $atts['class'] ? ' class="' . esc_attr( $atts['class'] ) . '"' : '';

    return sprintf( '<a href="%s"%s%s>%s</a>', esc_url( $url ), $target, $class, $label );
}
add_shortcode( 'custom_link', 'custom_link_shortcode' );
記事本文での使用例
<!-- 基本的な使い方 -->
[custom_link key="custom_url" text="参考サイトはこちら"]

<!-- 同じタブで開く -->
[custom_link key="custom_url" text="詳しくはこちら" new_tab="false"]

<!-- CSSクラスを付与 -->
[custom_link key="download_url" text="ダウンロード" class="btn btn-primary"]

関連記事:オリジナルのカスタムショートコードを作成する方法

WP_Queryでカスタムフィールドの有無によるフィルタリング

「URLが入力されている投稿だけを表示したい」「特定のURLを含む投稿を検索したい」場合は、WP_Querymeta_queryを使います。

URLが存在する投稿を取得
<?php
$args = array(
    'post_type'      => 'post',
    'posts_per_page' => 10,
    'meta_query'     => array(
        array(
            'key'     => 'custom_url',
            'value'   => '',
            'compare' => '!=',
        ),
    ),
);

$query = new WP_Query( $args );

if ( $query->have_posts() ) :
    while ( $query->have_posts() ) : $query->the_post();
        $url = get_post_meta( get_the_ID(), 'custom_url', true );
        echo '<li><a href="' . esc_url( $url ) . '">' . get_the_title() . '</a></li>';
    endwhile;
    wp_reset_postdata();
endif;
?>

特定ドメインのURLを含む投稿を検索

LIKEで部分一致検索
$args = array(
    'post_type'  => 'post',
    'meta_query' => array(
        array(
            'key'     => 'custom_url',
            'value'   => 'example.com',
            'compare' => 'LIKE',
        ),
    ),
);
// → custom_url に「example.com」を含む投稿を取得

関連記事:WP_Queryでカスタムフィールドを複数条件でソートする方法カスタムフィールドを検索対象にする方法

register_post_meta()でREST API対応にする

WordPress 4.9.8以降では、register_post_meta()を使ってカスタムフィールドをREST APIに公開できます。ブロックエディタ(Gutenberg)との連携にも必要です。

functions.php
function register_custom_url_meta() {
    register_post_meta( 'post', 'custom_url', array(
        'show_in_rest'      => true,
        'single'            => true,
        'type'              => 'string',
        'sanitize_callback' => 'sanitize_url',
        'auth_callback'     => function() {
            return current_user_can( 'edit_posts' );
        },
    ) );
}
add_action( 'init', 'register_custom_url_meta' );
REST APIからの取得・更新
// 取得:GET /wp-json/wp/v2/posts/123
// → レスポンスの meta.custom_url にURLが含まれる

// 更新:POST /wp-json/wp/v2/posts/123
// リクエストボディ:
{
    "meta": {
        "custom_url": "https://example.com/new-url"
    }
}

? ポイント:register_post_meta()sanitize_callback を設定すると、REST API経由での更新時にも自動でサニタイズが適用されます。auth_callback は認証チェックで、未認証ユーザーによる更新を防ぎます。

パフォーマンス:get_post_meta のキャッシュの仕組み

get_post_meta() は内部で wp_cache(オブジェクトキャッシュ)を利用しています。同一リクエスト内での動作を理解すると、無駄な最適化を避けられます。

タイミング 動作 DBクエリ
投稿をクエリした時点 その投稿の全メタデータが一括でキャッシュに読み込まれる 1回(全メタ一括取得)
1回目の get_post_meta() キャッシュから取得(DBアクセスなし) 0回
2回目以降の get_post_meta() 同じくキャッシュから取得 0回
update_post_meta() 後 キャッシュが更新される 1回(UPDATE文)
変数にキャッシュする必要はない
// ❌ 不要な最適化(get_post_meta は内部キャッシュがある)
$meta_cache = get_post_meta( get_the_ID() ); // 全メタを変数にキャッシュ
$url  = $meta_cache['custom_url'][0] ?? '';
$text = $meta_cache['custom_text'][0] ?? '';

// ✅ そのまま複数回呼んでOK(DBアクセスは発生しない)
$url  = get_post_meta( get_the_ID(), 'custom_url', true );
$text = get_post_meta( get_the_ID(), 'custom_text', true );

? ポイント:ループ内で同じ投稿の get_post_meta() を複数回呼んでも、パフォーマンスに影響はほぼありません。ただし、ループ外で別の投稿のメタを大量に取得する場合update_meta_cache() で事前にプリフェッチすると効率的です。

実務で使える7つのパターン

パターン1:参考リンクセクション

記事の末尾に参考リンクをカード型で表示するパターンです。

参考リンクカード
<?php
$url  = get_post_meta( get_the_ID(), 'ref_url', true );
$text = get_post_meta( get_the_ID(), 'ref_text', true );
$desc = get_post_meta( get_the_ID(), 'ref_desc', true );

if ( $url ) : ?>
<div style="border:1px solid #e2e8f0;border-radius:8px;padding:1em;margin:1em 0;">
    <p style="margin:0 0 0.3em;font-size:12px;color:#64748b;">? 参考リンク</p>
    <a href="<?php echo esc_url( $url ); ?>" target="_blank"
       rel="noopener noreferrer"
       style="font-weight:bold;text-decoration:none;">
        <?php echo esc_html( $text ?: $url ); ?>
    </a>
    <?php if ( $desc ) : ?>
        <p style="margin:0.3em 0 0;font-size:13px;color:#64748b;">
            <?php echo esc_html( $desc ); ?>
        </p>
    <?php endif; ?>
</div>
<?php endif; ?>

パターン2:ダウンロードボタン

ダウンロードボタン
<?php
$download_url = get_post_meta( get_the_ID(), 'download_url', true );

if ( $download_url ) : ?>
    <a href="<?php echo esc_url( $download_url ); ?>"
       download
       class="download-btn"
       style="display:inline-block;padding:12px 24px;
              background:linear-gradient(135deg,#3b82f6,#2563eb);
              color:#fff;border-radius:8px;text-decoration:none;
              font-weight:bold;transition:transform 0.2s;">
        ⬇ ダウンロード
    </a>
<?php endif; ?>

パターン3:SNSプロフィールリンク(著者情報)

SNSリンクアイコン表示
<?php
$sns_links = array(
    'twitter_url'  => array( 'label' => 'X (Twitter)', 'icon' => '?' ),
    'github_url'   => array( 'label' => 'GitHub',     'icon' => '?' ),
    'website_url'  => array( 'label' => 'Website',    'icon' => '?' ),
);

echo '<div class="sns-links" style="display:flex;gap:12px;">';
foreach ( $sns_links as $key => $info ) {
    $url = get_post_meta( get_the_ID(), $key, true );
    if ( $url && filter_var( $url, FILTER_VALIDATE_URL ) ) {
        printf(
            '<a href="%s" target="_blank" rel="noopener" title="%s">%s</a>',
            esc_url( $url ),
            esc_attr( $info['label'] ),
            $info['icon']
        );
    }
}
echo '</div>';
?>

パターン4:外部リンクへのリダイレクト

投稿のパーマリンクを外部URLに置き換えるパターンです。アフィリエイトリンクの管理などに使えます。

functions.php(パーマリンク置換)
/**
 * カスタムフィールドにURLがあればパーマリンクを置換
 */
add_filter( 'post_link', function( $permalink, $post ) {
    $external = get_post_meta( $post->ID, 'external_url', true );

    if ( $external && filter_var( $external, FILTER_VALIDATE_URL ) ) {
        return esc_url( $external );
    }

    return $permalink;
}, 10, 2 );

パターン5:投稿ヘッダーにOGP画像URLを設定

OGP画像をカスタムフィールドで指定
add_action( 'wp_head', function() {
    if ( ! is_singular() ) return;

    $ogp_url = get_post_meta( get_the_ID(), 'ogp_image_url', true );

    if ( $ogp_url && filter_var( $ogp_url, FILTER_VALIDATE_URL ) ) {
        printf(
            '<meta property="og:image" content="%s">' . "\n",
            esc_url( $ogp_url )
        );
    }
} );

関連記事:投稿ごとにOGP画像を設定するカスタムフィールドの作成方法

パターン6:条件付き表示(値なしの場合の処理)

値なし時のフォールバック
<?php
$url = get_post_meta( get_the_ID(), 'custom_url', true );

if ( $url ) {
    // URLが入力されている → 外部リンク
    printf(
        '<a href="%s" target="_blank" rel="noopener">詳細はこちら(外部サイト)</a>',
        esc_url( $url )
    );
} else {
    // URLが未入力 → 投稿自体のパーマリンク
    printf(
        '<a href="%s">詳細はこちら</a>',
        esc_url( get_permalink() )
    );
}
?>

関連記事:カスタムフィールドに値が入っていない記事を取得する方法カスタムフィールドの入力有無に応じて表示を切り替える方法

パターン7:管理画面の投稿一覧にURL列を追加

functions.php
// カラム追加
add_filter( 'manage_posts_columns', function( $columns ) {
    $columns['custom_url'] = 'カスタムURL';
    return $columns;
} );

// カラム内容
add_action( 'manage_posts_custom_column', function( $column, $post_id ) {
    if ( $column === 'custom_url' ) {
        $url = get_post_meta( $post_id, 'custom_url', true );
        if ( $url ) {
            printf( '<a href="%s" target="_blank">%s</a>',
                esc_url( $url ),
                esc_html( wp_trim_words( $url, 5 ) )
            );
        } else {
            echo '—';
        }
    }
}, 10, 2 );

関連記事:管理画面の投稿一覧にカスタムフィールドの値を表示する方法

トラブルシューティング

症状 原因 対処法
get_post_meta() が空を返す キー名のタイポ、$single 未指定で配列が返っている キー名を正確に確認。var_dump( get_post_meta( $id ) ) で全メタデータを出力して確認
リンクをクリックしても遷移しない URLに https:// がない(例: example.com 保存時に esc_url_raw() を通すと自動で http:// が付与される。またはバリデーションで弾く
URLに &amp; が表示される 二重エスケープ(esc_url() を複数回呼んでいる) esc_url() は出力直前に1回だけ呼ぶ
ACFフィールドの値が取得できない get_post_meta()get_field() のキー名が異なる ACFは内部的に _ プレフィックス付きでメタキーを保存する。ACF使用時は get_field() を使う
ループ内で同じURLが出力される get_the_ID() がループ外のIDを参照 $post->ID を明示的に渡すか、wp_reset_postdata() を忘れていないか確認
メタボックスの値が保存されない nonce検証の失敗、自動保存時の処理、権限不足 nonce名・action名の一致を確認。DOING_AUTOSAVE チェックが正しいか確認
REST APIで meta が返らない register_post_meta()show_in_rest が未設定 show_in_rest => true を設定し、init フックで登録

まとめ

やりたいこと 方法 セキュリティ
基本的なURL取得・リンク表示 get_post_meta() + esc_url() 出力エスケープ
専用入力欄の作成 add_meta_box() + nonce + 権限チェック 3層防御
ACFでの実装 URLフィールド or リンクフィールド ACF内蔵バリデーション
記事本文での出力 ショートコード [custom_link] バリデーション + エスケープ
URLありの投稿を検索 WP_Query + meta_query
REST API連携 register_post_meta() sanitize_callback + auth_callback
複数URLの管理 $single=false or ACFリピーター ループ内で個別バリデーション

カスタムフィールドのURLをリンクとして表示する基本は get_post_meta() + esc_url() の組み合わせです。ここに入力サニタイズ(sanitize_url)バリデーション(FILTER_VALIDATE_URL)を加えた3層防御を意識することで、安全な実装になります。

実務では、メタボックスやACFで入力UIを整え、ショートコードやWP_Queryと組み合わせることで、柔軟かつ堅牢なURL管理が実現できます。

関連記事:投稿のカスタムフィールドを取得する方法get_post_meta完全ガイド投稿や固定ページのURLを取得する方法