「Debug では HTTPS で動くのに、Release/ClickOnce 公開後は HTTP に戻る」。原因の多くは、発行物に含まれる <app>.exe.config.deploy に古い app.config の断片が残っていることです。本稿では、古い設定を確実に一掃し、リリースでも最新の HTTPS を反映させるための再現性の高い手順と、根本理解・再発防止までを一気通貫で解説します。
問題の全体像と前提
現象はシンプルですが、発生要因はいくつかの層(ビルド、発行、クライアント側キャッシュ)にまたがります。まずは状況を共通認識化しましょう。
- Debug 実行(F5)では HTTPS に接続できる。
- Release ビルドで発行(ClickOnce)した実行では HTTP に戻ってしまう。
- 発行物に含まれる
<app>.exe.config.deployに、削除済みのhttp://...が残っている。
このとき、単に app.config を直しても、発行物が「差分コピー」や「古い生成物の再利用」により更新されず、古い値が紛れ込むことがあります。
先に結論:確実に直す最短ルート
迷ったら、次のフルリフレッシュ手順を実行してください。これで大半の案件は解決します。
- Visual Studio を一旦終了する(開いたままだと一部ファイルがロックされます)。
- プロジェクトルートから以下のフォルダーを手動削除する:
publish(またはapp.publish)binobj- 隠しフォルダー
.vs
- Visual Studio を再起動し、ソリューションを開く。
- Release 構成を選択し、再ビルド。
- ClickOnce を再公開(Publish)。
ポイントは、「差分ビルド・差分発行」を完全に回避し、.exe.config.deploy を app.config から再生成させることです。
チェックリスト(削除ターゲットと目的)
| 場所 | 削除理由 | 注意点 |
|---|---|---|
bin | 過去の成果物(.exe.config 等)が残って差分コピーされるのを防ぐ | 外部に配布したファイルは残りません。必要ならバックアップ後削除 |
obj | 中間生成物(結合後 .config、キャッシュ)を一掃 | ソリューション全体で削除すると効果が高い |
publish / app.publish | ClickOnce の発行物を再生成させる | 都度生成されるので削除して問題なし |
.vs(隠し) | Fast Up-to-date Check 等のキャッシュをクリア | Visual Studio を閉じてから削除 |
なぜ起きる?(仕組みの理解)
ClickOnce 発行では、プロジェクトの app.config がビルド時に <アセンブリ名>.exe.config に変換され、発行時に(設定により).deploy 拡張子が付与されて配置されます。古い生成物が残った状態で差分発行が行われると、更新された設定が発行物に反映されないことがあります。また、Debug と Release で 別々の中間生成物が維持されるため、「Debug は正しいが Release だけ古い」という非対称が発生します。
現象の再現と切り分けのコツ
- Release のみで起きるか?(構成固有設定/プロファイルの差を疑う)
- 発行フォルダー内の
<app>.exe.configと.deployの更新日時と内容を比較する - クライアント端末の ClickOnce アプリケーションキャッシュをクリアして挙動を確認する
詳細解決手順(段階的に深掘り)
ビルド生成物・公開物の「完全削除 → 再生成」
GUI 操作が難しい環境では、以下のコマンドで同等のクリーンを行えます(プロジェクト直下で実行)。
dotnet clean
dotnet build -c Release
dotnet publish -c Release
.NET Framework ベースのソリューションで msbuild を使う場合は次のようにまとめても構いません。
msbuild YourSolution.sln /t:Clean;Build /p:Configuration=Release
発行は Visual Studio の「発行」からでも、発行プロファイル(.pubxml)を使ってコマンドラインからでも OK です。
プロジェクトファイル(.csproj)を直接確認
旧 URL・旧サービス名が 構成固有のプロパティや発行設定に残っていないかをチェックします。
- ソリューション エクスプローラーでプロジェクトを右クリック →「プロジェクトのアンロード」→ もう一度右クリック →「プロジェクト ファイルの編集」。
http://、旧サービス名などのキーワードで検索。- 特に
Release条件付きのPropertyGroup、および発行関連の要素に注意。
例(Release 条件で紛れ込むケース):
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PublishUrl>C:\PublishOutput</PublishUrl>
<InstallUrl>http://old.example.local/app/</InstallUrl> <!-- ← 旧URLが残存 -->
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>true</UseApplicationTrust>
<MapFileExtensions>true</MapFileExtensions> <!-- .deploy 付与 -->
</PropertyGroup>
また、Properties\PublishProfiles\*.pubxml 側にも発行先や InstallUrl が記載されます。発行プロファイルの中に旧 URL が残っていると、アプリの自己参照リンクや更新元 URL が古いまま維持され、結果として旧環境に誘導されることがあります。
サービス参照/設定ファイルの連動を再点検
ASMX/WCF の古い参照を使っている場合、次の複数地点に URL が分散して残ることがあります。
Service References\{参照名}\Reference.svcmapService References\{参照名}\*.config(自動マージ対象)Properties\Settings.settings(ユーザー設定に URL を格納している場合)app.config(system.serviceModel、applicationSettings)
Visual Studio の「サービス参照の更新」だけでは反映されない場合があるため、テキスト検索で一掃してください。
クライアント端末の ClickOnce キャッシュをクリア
サーバー側・発行側が正しくても、クライアント端末に古い発行物がキャッシュされていると復元されることがあります。安全にクリアするコマンドは以下です。
rundll32 dfshim CleanOnlineAppCache
Windows SDK を導入済みなら次でも可能です:
mage -cc
キャッシュの物理パスは一般に %LOCALAPPDATA%\Apps\2.0\ 配下です。手動削除は推奨しませんが、やむを得ない場合はアプリを終了したうえで慎重に実施してください。
どの .config が発行されたかを確かめる(検証)
発行フォルダーとソースの app.config を比較し、期待する差分のみであることを確かめます。PowerShell ならハッシュ比較が簡単です。
# ソース側(ビルド後の出力)
Get-FileHash ".\bin\Release\<app>.exe.config"
# 発行物(.deploy の場合は拡張子を除いて比較)
Get-FileHash ".\publish<app>.exe.config"
ClickOnce で .deploy を付けている場合は、拡張子以外の中身が一致するか(または HTTPS に書き変わっているか)を確認します。
Release 専用の混入ポイント早見表
| 混入ポイント | 症状 | 対処 |
|---|---|---|
.pubxml(発行プロファイル) | InstallUrl、更新元 URL が旧値 | 該当プロファイルを編集/作り直す |
.csproj の Release 条件 | Release のみ旧サービス名で上書き | 検索で一括置換、不要行を削除 |
| サービス参照(WCF/ASMX) | Reference.svcmap 等に旧 URL | 参照の更新+手動で URL の残骸を削除 |
ユーザー設定(Settings.settings) | ユーザー作用で旧値を保持 | 初期化処理を入れる/設定名を変える |
| ClickOnce クライアントキャッシュ | 古い発行物にロールバック | キャッシュクリア(dfshim または mage -cc) |
根本原因をもう一歩踏み込んで理解する
Visual Studio のビルドは「高速アップトゥデートチェック」により、入力・出力の更新日時や依存関係から「再生成不要」と判断することがあります。app.config の変更が間接的・条件付きでしか伝搬しない構成(例:構成別プロパティや発行プロファイルに依存)だと、.exe.config.deploy の再生成が省略され、旧ファイルがそのまま発行されます。完全削除によりこの判断根拠(中間生成物とタイムスタンプ)を消すのが強力な理由です。
再発防止:環境別設定の「明示管理」
「Debug は動き、Release で戻る」問題の多くは、暗黙の差分に起因します。設定の明示化で再発を予防しましょう。
- 構成変換(Transform)を導入:
app.Debug.config/app.Release.configを用意し、HTTPS や API エンドポイントを構成ごとに宣言的に管理(拡張機能やビルドタスクで適用)。 - CI パイプラインに Clean を必ず入れる:
dotnet clean→dotnet build -c Release→dotnet publish -c Releaseを固定フローに。 - バージョン管理に発行プロファイルを含める:
Properties\PublishProfilesをリポジトリに入れて、差分レビューで旧 URL 混入を検出。 - ユーザー設定の刷新:既存
applicationSettings/userSettingsに旧値が残る場合、設定名を変えて初期値を新 URL にし、初回起動で旧設定をマイグレーション。
安全策:ユーザー設定に頼らないエンドポイント切り替え
app.config からの読み込みをそのままユーザー設定へ落とし込むと、ユーザー環境に旧値が固定されることがあります。次のいずれかの方針が堅実です。
- 設定はアプリケーション設定固定(ユーザーが変更不可)にして、更新は再発行でのみ行う。
- 初回起動時に、既定値と比較して旧値なら強制上書きするマイグレーション処理を導入。
例(C# 擬似コード):
var url = Properties.Settings.Default.ServiceUrl;
if (url.StartsWith("http://"))
{
Properties.Settings.Default.ServiceUrl = "https://api.example.com/";
Properties.Settings.Default.Save();
}
デバッグ術:MSBuild ログで「何が発行されたか」を可視化
ビルドや発行の経路を辿るには MSBuild のバイナリログが便利です。
msbuild YourProject.csproj /t:Publish /p:Configuration=Release /bl:build.binlog
生成された build.binlog をロガービューアで開き、<app>.exe.config や .deploy をキーワードに検索すると、「どこからコピーされ、いつ上書きされたか」が一目でわかります。Release だけ別のファイルが参照されている場合もここで発見できます。
運用チェックリスト(配布前に)
| 確認項目 | 方法 | 合格基準 |
|---|---|---|
.exe.config.deploy の URL | テキスト検索(http://) | http:// が 0 件、https:// は期待どおり |
| 発行プロファイル | .pubxml を目視 | InstallUrl 等に旧 URL がない |
| ClickOnce クライアント挙動 | テスト端末で新規インストール | HTTPS で通信、イベントログに警告なし |
| ユーザー設定 | 初回起動~再起動 | 旧値が復活しない(マイグレーション成功) |
補足:ありがちな誤解と対処
- 「
app.configを直したのに直らない」:ビルド後の<app>.exe.configと発行物の.deployを別個に確認。発行工程で旧ファイルに差し替わっている可能性。 - 「
.deployをやめれば直る?」:拡張子は可視性の違いで、中身の更新有無とは無関係。原因の本質は差分発行とキャッシュ。 - 「サービス名を変えたら動いた」:動作はするが、旧設定がどこかに残っているサイン。根っこを断つ(検索・削除・再生成)。
- 「Debug では OK なのに…」:
DebugとReleaseのPropertyGroupの差異を必ず比較。
PowerShell ワンライナー(掃除+確認)
手作業を減らすための簡易スクリプト例です。
# 1) 生成物の一掃
@('bin','obj','app.publish','publish','.vs') | % { if (Test-Path $_) { Remove-Item $_ -Recurse -Force -ErrorAction SilentlyContinue } }
# 2) Release ビルド&発行(必要に応じてプロジェクト名を調整)
dotnet clean
dotnet build -c Release
dotnet publish -c Release -o publish
# 3) http が紛れ込んでいないかチェック
Select-String -Path .\publish* -Pattern 'http://'
ケーススタディ:複数プロジェクト構成での落とし穴
ソリューション内に UI(WinForms/WPF)とクラスライブラリが分かれている場合、app.config は UI プロジェクトのものが最終合成に使われます。しかしライブラリ側の App.config 断片(App.config として誤って含めた XML)が Content 扱いでコピーされると、最終的に <app>.exe.config と並んで「別名の .config」が紛れ込み、紛らわしいトラブルになります。ライブラリ側には app.config を置かない、置くなら ビルドアクションを None にする、が鉄則です。
高度な検証:発行物のバイトレベル差分
どうしても原因を特定したい場合は、発行前後のファイルをテキスト比較ツールや fc コマンドで比較します。
fc /n /a ".\bin\Release\<app>.exe.config" ".\publish\<app>.exe.config"
わずかな差分(スペースやコメント)ではなく、http:// の有無やエンドポイントの差があるかに注目します。
まとめ(実務フロー)
同種の不具合を最短で収束させるには、次の順で確認すると迷いません。
- 生成物の完全削除(
bin/obj/publish/.vs)。 - Release で再ビルド&再発行。
.exe.config.deployを直接確認(http://を検索)。.csprojと.pubxmlの旧 URL を除去。- クライアントの ClickOnce キャッシュをクリア。
- サービス参照/
Settings.settingsの旧値を整理。 - CI に Clean → Build → Publish を固定化、設定は構成変換で明示管理。
この流れなら、旧 URL が混入した .exe.config.deploy を確実に排除でき、リリースでも最新の HTTPS 設定が反映されます。いったん整理して仕組みを理解しておけば、次回以降は「クリーン→再生成」で短時間に復旧できます。
付録:トラブルの未然防止テンプレート
プロジェクト作成直後に以下の整備をしておくと、将来の構成差異を安全に運べます。
- 命名規則:設定キーやサービス参照名に環境名(Prod/Dev)を含めない。環境は Transform かプロファイルで切替。
- 発行プロファイル:
FolderProfile-Release.pubxmlなど分かりやすく命名し、InstallUrlを環境変数から注入する設計に。 - ビルドスクリプト:
build.ps1に Clean→Build→Publish を固定化。 - レビュー観点:Pull Request のテンプレートに「
http://は残っていないか」をチェック項目として追加。
付録:よくある Q&A
Q. .deploy 拡張子は必須ですか?
A. いいえ。MapFileExtensions を false にすれば付けない運用も可能です。拡張子の有無はファイアウォールやプロキシの都合で選びますが、今回の問題(古い設定の残留)と直接の因果はありません。
Q. 旧 URL が見当たらないのに挙動が戻ります。
A. ユーザー設定(user.config)や端末の ClickOnce キャッシュに古い値が残っている可能性が高いです。マイグレーション処理とキャッシュクリアを試してください。
Q. 複数の発行プロファイルを使い分けています。
A. 使わないプロファイルにも旧 URL が残っていると、誤って選択したときに再発します。不要プロファイルは削除し、必要なものだけをバージョン管理しましょう。
Q. app.config の Transform は標準で効きますか?
A. ツールやビルドタスクで適用するのが一般的です。構成別の設定ファイルを用意し、ビルド時に確実にマージされるパイプラインを整えるのが安全です。
実運用の一言メモ
「Debug では動くのに Release で戻る」は、経験上ほぼ生成物の残骸か構成差分の見落としです。迷ったらまずは全削除 → 再生成。そのうえで .csproj/.pubxml/サービス参照/ユーザー設定の 4 点セットを点検すれば、短時間で収束できます。

コメント