【第19回】本文内の画像URLを自動サムネイル展開!安全なプロキシ経由とモーダルプレビューの実装
💡 この記事のまとめ
ブログやSNS、チャットツール開発で必須の「画像URL自動サムネイル化」。セキュリティを守るプロキシ構築と、UXを爆上げするモーダル表示の実装方法を解説します。AI副業での応用方法も公開!
本文内の画像URLを自動サムネイル展開!安全なプロキシ経由とモーダルプレビューの実装
こんにちは!AI技術を駆使してWebサービスを開発し、マネタイズする「AI副業」の世界へようこそ。
近年、チャットツールやブログ、SNSなどのWebサービスにおいて、**「ユーザーが投稿した画像URLを自動で検知し、サムネイルとして表示する機能」**はもはや標準装備となっています。リンクをクリックせずとも視覚的に情報が伝わるため、ユーザーの滞在時間を延ばし、サイトの価値(ひいては広告収入や成約率)を大きく高めることができます。
しかし、この機能を何も考えずに実装すると、**「セキュリティの脆弱性(SSRFなど)」や「外部画像への直接アクセスによるロード遅延、リンク切れ」**といった深刻な問題に直面します。
そこで今回は、**「安全なプロキシサーバーを経由した画像取得」と、「クリックで美しく拡大するモーダルプレビュー」**を組み合わせた、安全でUX(顧客体験)の高いサムネイル自動展開システムの実装方法を徹底解説します!
この技術をマスターすれば、クライアントワークでの単価アップや、自身で運営するアフィリエイトメディア、SNS風サービスの差別化に直結します。
1. 今回実装するコンセプトと全体像
まずは、今回構築するシステムの全体像を理解しましょう。
① 本文内の画像URL自動抽出
ユーザーが入力したテキスト(Markdownやプレーンテキスト)から、正規表現を用いて「画像ファイルの拡張子(.jpg, .png, .gif, .webpなど)を持つURL」を自動検知します。
② 安全な画像プロキシ(Proxy)の経由
外部の画像URLを直接 <img> タグに設定すると、以下のリスクが生じます:
- ユーザーのIPアドレスが外部サーバーに漏洩する(プライバシー問題)
- HTTPSのサイト内にHTTPの画像が混ざり、セキュリティ警告が出る(Mixed Content)
- 相手サーバーからアクセス拒否(CORSエラー)される
これらを防ぐため、自前のAPI(プロキシ)を1つ挟み、サーバー側で画像を一度取得・キャッシュしてから、安全な形でクライアントに配信します。
③ モーダルプレビューによる快適なUX
展開されたサムネイルをクリックすると、画面全体が暗くなり、滑らかなアニメーションと共に大画面で画像が表示される「モーダルプレビュー」を実装します。これにより、サイトからの離脱を防ぎつつ、詳細な情報をユーザーに提供できます。
2. 実装コード解説(Next.js / React & Tailwind CSS)
今回は、現在モダンなWeb開発で主流となっている Next.js (App Router) と Tailwind CSS を使った実装例をご紹介します。非常にシンプルかつセキュアに構築できます。
ステップ1:セキュアな画像プロキシAPIの作成
まずはサーバーサイドで外部画像を安全に取得するAPIルーティングを作成します。
ファイルパス: app/api/proxy/route.js
javascript import { NextResponse } from 'next/server';
export async function GET(request) { const { searchParams } = new URL(request.url); const imageUrl = searchParams.get('url');
if (!imageUrl) { return new NextResponse('Missing URL parameter', { status: 400 }); }
try { // SSRF対策:ローカルホスト等のIPへのアクセスを制限する簡易チェック if (imageUrl.startsWith('http://localhost') || imageUrl.startsWith('http://127.0.0.1')) { return new NextResponse('Blocked suspicious URL', { status: 403 }); }
const response = await fetch(imageUrl, {
headers: {
'User-Agent': 'SafeImageProxy/1.0',
},
});
if (!response.ok) throw new Error('Failed to fetch image');
const contentType = response.headers.get('content-type');
// 画像フォーマットのみを許可
if (!contentType || !contentType.startsWith('image/')) {
return new NextResponse('Invalid content type', { status: 400 });
}
const buffer = await response.arrayBuffer();
return new NextResponse(buffer, {
headers: {
'Content-Type': contentType,
'Cache-Control': 'public, max-age=86400', // 1日キャッシュ
},
});
} catch (error) { console.error('Proxy Error:', error); return new NextResponse('Error fetching image', { status: 500 }); } }
ステップ2:テキスト解析 & モーダル付きコンポーネントの実装
次に、テキストから画像URLを検出し、プロキシ経由のサムネイルを表示、クリックでモーダルを開くUIパーツを作成します。
ファイルパス: components/RichTextRenderer.jsx
jsx 'use client';
import { useState } from 'react';
export default function RichTextRenderer({ text }) { const [selectedImage, setSelectedImage] = useState(null);
// 画像URL(http〜webp等)を検出する正規表現 const imageRegex = /(https?://[^\s]+.(?:png|jpg|jpeg|gif|webp))/gi;
// テキストを改行などで分割し、URLがあればサムネイルを展開する処理
const renderContent = () => {
const parts = text.split(imageRegex);
return parts.map((part, index) => {
if (part.match(imageRegex)) {
// 安全なプロキシ用のURLを生成
const proxyUrl = /api/proxy?url=${encodeURIComponent(part)};
return (
<div key={index} className="my-4">
<img
src={proxyUrl}
alt="Thumbnail Preview"
className="max-w-xs h-auto rounded-lg shadow-md cursor-pointer hover:opacity-90 transition duration-200"
onClick={() => setSelectedImage(proxyUrl)}
/>
<span className="text-xs text-gray-500 block mt-1 break-all">{part}</span>
</div>
);
}
return <span key={index} className="whitespace-pre-wrap">{part}</span>;
});
};
return ( <div className="p-4 border rounded-xl bg-white shadow-sm"> <div className="text-gray-800 leading-relaxed">{renderContent()}</div>
{/* モーダルプレビュー画面 */}
{selectedImage && (
<div
className="fixed inset-0 bg-black/80 flex items-center justify-center z-50 animate-fade-in"
onClick={() => setSelectedImage(null)}
>
<div className="relative max-w-4xl max-h-[90vh] p-2">
<button
className="absolute top-4 right-4 text-white bg-black/50 hover:bg-black/80 w-10 h-10 rounded-full flex items-center justify-center text-xl font-bold transition"
onClick={() => setSelectedImage(null)}
>
✕
</button>
<img
src={selectedImage}
alt="Full Preview"
className="max-w-full max-h-[85vh] object-contain rounded"
/>
</div>
</div>
)}
</div>
); }
3. この技術を活用した「AI副業」マネタイズガイド
この安全な画像プレビュー機能をどのように収益化に結びつけるのか、具体的なステップを紹介します。
ステップ1:特化型「まとめ・キュレーションサイト」の構築
AI(ChatGPTやClaude)を使って、海外の有益なテック情報やデザインのトレンド情報を自動翻訳・収集します。 その際、本文中のデモ画像を自動サムネイル表示させ、ユーザーがクリックしてモーダルで確認できる高機能な「まとめサイト」を構築します。視覚的な美しさと安全性が担保されたサイトは、滞在時間が非常に長くなり、Google AdSenseやAmazonアソシエイトなどの広告収入を最大化できます。
ステップ2:クライアントワーク(Web制作)の単価アップ
企業のブログシステムや、コミュニティサイトを制作する際、ただテキストを表示するだけでなく、
「セキュリティ対策を施した、画像リンクの自動プレビュー機能も標準で実装できますよ」
と提案します。これにより、競合エンジニアと差別化でき、開発単価を数万円〜数十万円上乗せして受注することが可能になります。
ステップ3:有料コミュニティ・SaaSの開発
Notionのように「URLを入力したらリッチなブログカードや画像プレビューが生成される」機能を組み込んだ、独自のクローズドな情報共有ツールや、特定のニッチ市場に向けた有料フォーラムを運営・販売(SaaS化)します。
4. メリットとデメリット
メリット
- 圧倒的なUX向上:ユーザーがいちいち別タブで画像を開くストレスがゼロになります。
- 高いセキュリティ:ユーザーのIPアドレスを保護し、SSRF(Server-Side Request Forgery)などの攻撃からサーバーとユーザーを守ります。
- CORS問題の回避:サーバーが代わりに取得するため、ブラウザ側での読み込みエラーが発生しません。
デメリット
- サーバー負荷の増加:大量の画像アクセスがプロキシに集中すると、自社サーバーの転送量が増大します。これには「CDN(Cloudflare等)による画像キャッシュ」を併用することで解決できます。
- 悪意ある画像の取得リスク:ウイルスが仕込まれたファイルをプロキシが読み込むリスク。これは、MIMEタイプ(Content-Type)チェックを厳格化することで防ぎます。
5. まとめ
「本文内の画像URL自動サムネイル展開」は、一見シンプルですが、「セキュリティ(プロキシ)」と「使いやすさ(モーダル)」を高いレベルで両立させて初めて完成するプロの技術です。
AIを駆使してテキストコンテンツを大量生成・配信できる時代だからこそ、こうした「ガワ(システムやUI/UX)」のクオリティに投資することで、競合に大きな差をつけることができます。
ぜひ今回のコードを参考に、ご自身のWebサービスに実装し、より価値の高いWebサイト構築(AI副業の収益化)に役立ててください!
次回は、さらに一歩進んで「画像だけでなく、YouTubeやTwitter(X)の埋め込みを自動で安全に行う方法」についてお届けします。お楽しみに!