AIで作るHTMLテンプレート商品、オフライン配布のつもりが外部参照を残す

ダウンロードしてすぐ開ける、1 枚の HTML テンプレートを商品にするつもりでした。見た目を整えるために外部の CSS を読み、利用状況を見るために外部の script を置くと、単一ファイルでも外に依存します。さらに送信先が/orderのフォームは、ローカルで開いた時に使い道がありません。
2026 年 10 月 3 日、こうした AI 生成ページで起こりがちな混入を、再現用の短い HTMLで検査しました。実際の顧客データや販売中の商品は使っていません。最初の検査は 4 件、依存を外した版は 0 件です。ただし、0 件は「この検査が見る箇所に問題がない」という意味に限ります。
前回完了時と今回完了後
前回は商品カバーの PNG を WebP へ変換し、来歴の印が残るか確かめました。画像ファイルの出所を見ましたが、配布する HTML が外部に何を求めるかは見ていません。
| 時点 | 確認したもの | 残る課題 |
|---|---|---|
| 前回完了時 | 商品カバーの署名とXMPを変換前後で確認しました | HTMLの参照先は対象外です |
| 今回完了後 | HTMLのsrc、href、actionとscriptの有無を確認しました | CSS内やJavaScript内の通信は別に調べます |
飛ばせる章: HTMLの参照先の仕組みを知っている場合は、「同じ検査を修正前後に通す」から進めます。
なぜ単一ファイルを先に疑うのか
「HTML を 1 枚渡せば、購入者はネット接続なしで使える」という前提は、ファイル数だけでは決まりません。<link>は外部のスタイルシートを参照できます。<script src>も別の場所のコードを読みます。<form action>は送信先を指定します。見た目を確認するだけでは、参照先が商品に含まれているか分かりません。
今回の受け入れ条件は狭くしました。配布候補の HTML にhttp://、https://、//で始まる参照がないこと、サイトのルートを前提とする/始まりの参照がないこと、そして script がないことです。外部 URL そのものが悪いのではありません。今回は「単一 HTML として持ち歩ける商品」を作るので、依存の有無を先に確かめます。
[AIを使ってHTML商品の案を作る]
↓
[人が配布候補を1ファイルへ整理]
↓
[HTMLの参照先とscriptを静的検査]
├─ findings>0 → 依存を外すか、同梱方針を決める
└─ findings=0 → 別途、ブラウザでオフライン操作を確認
↓
[人が販売可否を判断]
用語
| 用語 | この回での意味 |
|---|---|
| 外部参照 | 商品ファイルの外にあるURLへ、HTMLがアクセスする指定です |
| ルート相対パス | /orderのように先頭が/のパスです。商品フォルダ内の相対パスとは違います |
src | scriptや画像などの参照元を記すHTML属性です |
href | linkやaなどの参照先を記すHTML属性です |
action | フォームの送信先を記すHTML属性です |
| 静的検査 | ブラウザを起動せず、ファイルの記述を調べる方法です |
HTMLParser | Python標準ライブラリのHTML解析用クラスです |
同じ検査を修正前後に通す
試した環境の Python は次の版です。
python3 --version
Python 3.13.5
下のコマンドはファイルを作らず、2 つの再現用 HTML をメモリ上で解析します。example.invalidは接続先として使うためではなく、外部参照がどこにあるかを示すダミーです。通信は行いません。
python3 -c 'from html.parser import HTMLParser
class Audit(HTMLParser):
def __init__(self):
super().__init__(); self.findings=[]
def handle_starttag(self, tag, attrs):
fields=dict(attrs)
if tag=="script": self.findings.append("script: review required")
for key in ("src", "href", "action"):
value=fields.get(key, "") or ""
if value.startswith(("http://", "https://", "//")):
self.findings.append(f"{tag}.{key}: remote {value}")
elif value.startswith("/"):
self.findings.append(f"{tag}.{key}: root path {value}")
for name, body in [("before", "<link rel=stylesheet href=https://cdn.example.invalid/theme.css><form action=/order><script src=https://metrics.example.invalid/pixel.js></script>"), ("after", "<style>body{color:#17324d}</style><p>問い合わせ先: [email protected]</p>")]:
audit=Audit(); audit.feed(body)
print(f"{name}: findings={len(audit.findings)}")
for finding in audit.findings: print(" " + finding)'
before: findings=4
link.href: remote https://cdn.example.invalid/theme.css
form.action: root path /order
script: review required
script.src: remote https://metrics.example.invalid/pixel.js
after: findings=0
初稿に相当するbeforeでは、CSS と script が商品外を指します。/orderは配布物の中にあるファイル名ではなく、サイトのルートを前提にした指定です。script には、外部 URL の指摘とは別に「コードの中身を確認する必要がある」という指摘も付けました。
修正版のafterでは、見た目の指定を<style>へ移し、使えない送信フォームと script を外しました。問い合わせ先は表示用の文字にしました。これで失う機能もあります。フォーム送信やアクセス計測が商品に不可欠なら、削って「完成」にするのではなく、ホストするサービスと依存先を設計し直します。
0件でも「通信しない証明」にはならない
この検査は HTML の開始タグにある 3 つの属性しか読みません。たとえば、<style>内の@importやurl()、インラインのイベント属性、srcset、文書を移動させる meta 要素は対象外です。script は内容を解析せず、存在するだけで指摘します。HTMLParser自体も、ブラウザの動作を再現するものではありません。
after: findings=0を「すべての通信がゼロ」と読むのは誤りです。今回は再現例を使った小さな受け入れ検査を作っただけで、商品全体をオフラインで操作した証拠はありません。実物を作ったら、配布するファイルを開き、ネットワークを切った状態で操作を試します。必要なら開発者ツールの Network も見ます。
| 確認手段 | 分かること | 分からないこと |
|---|---|---|
| このPython検査 | 指定したHTML属性の外部参照とscriptの有無です | CSS内部や動的通信は分かりません |
| オフラインでの操作 | 接続なしで基本画面を使えるか分かります | あらゆる操作経路の網羅はできません |
| ブラウザのNetwork確認 | 操作時の実際の要求先を観察できます | 未操作の機能が何を送るかは分かりません |
販売前には、何を「オフラインで使える」と約束するかも書き分けます。閲覧だけできればよいのか、入力結果を保存する必要があるのかで、必要なテストは変わります。
よくあるエラー
| 症状 | 原因 | 対応 |
|---|---|---|
| 外部CSSの指摘が出ます | HTMLがCDNを参照しています | ライセンスを確認し、同梱または自前のCSSに切り替えます |
form.action: root path /orderと出ます | サイトの送信先をそのまま残しています | ローカル商品の仕様から送信を外すか、ホスト型商品として設計します |
| scriptの指摘が2件出ます | scriptの存在と外部srcを別々に数えます | 片方だけ消して合格扱いにしません |
findings=0なのに外部へ要求します | CSS内や別の属性が検査対象外です | ブラウザで操作し、Networkを確認します |
| 商品では表示が崩れます | 参照先を削っただけで必要なCSSを移していません | オフライン状態で画面を見直します |
mailto:が検査を通ります | この検査はメールクライアント起動を判定しません | 配布条件に合わせ、連絡導線を人が確認します |
一次情報
| 資料 | 確認したこと |
|---|---|
| Python: html.parser | HTMLParserを継承して開始タグと属性を読む方法と、HTMLの整合性までは検証しない制限です |
| MDN: link要素 | hrefで外部のスタイルシートを指定する仕組みです |
| MDN: script要素 | srcで外部スクリプトを参照できる仕組みです |
| MDN: form要素 | フォームのactionが送信先を指定することです |
| MDN: Content Security Policy | サイト運営者が読み込み先を制御するHTTPヘッダーの用途です。今回のローカルHTML検査とは別の対策です |
AI へ「シンプルなランディングページを」と頼むと、見栄えのよい HTML だけで完成した気になりがちです。私が今回変えたのは、文章の指示ではなく商品の受け入れ条件です。外部参照を見つけたら、削除だけで済ませず、その機能を誰が提供するのかまで決めます。


