1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Obsidian + 自宅WebDAVサーバでiPhone同期を実現した件

1
Last updated at Posted at 2026-04-01

はじめに

Notionを使っていましたが、Obsidian(オブシディアン:「黒曜石」の意味)に乗り換えました。Obsidianと自分のWebDAVサーバを組み合わせれば、iPhoneと自宅PCなどのマークダウンメモをクラウドサービスに依存せずに高度に連携できると思ったからです。
クラウドサービスが紛争で影響を受けたり、いくら暗号化されているとはいえ、業務のメモやプライベートな情報は、自分が管理するストレージに置きたい、と思ったのがきっかけでした。
Githubのソースコード修正までして、ようやく実現!(誰かほめてw)

なぜObsidianなのか

Obsidianは、単なるメモアプリではなく、Markdownベースの"自分専用知識ベース"を育てられる環境です。しかもローカルLLMやRAGとの相性がよく、生成AIで得た知見を一過性の会話で終わらせず、再利用可能な資産に変えやすいです。この点が、生成AI界隈で注目される大きな理由でもあります。

ファイルはただのMarkdownなので、Gitでバージョン管理もできますし、どんなエディタでも開けます。特定のクラウドサービスにロックインされないのが最大の利点です。

本記事では、自宅サーバにWebDAVを立てて、Windows版ObsidianとiPhone版Obsidianの双方向同期を実現した手順を紹介します。途中、iOS版でSSL接続が一切通らないという深刻な問題にぶつかり、Remotely Saveプラグインのソースコードを修正して解決しました。その全過程を記録します。

最終構成

最終的に動作した構成は以下の通りです。

iPhone Obsidian
  ↕ Remotely Save(修正版: fetch() for iOS)
  ↕ HTTPS
Nginx WebDAVサーバ(Ubuntu 24.04)
  ↕ HTTPS
  ↕ Remotely Save(オリジナル版: requestUrl())
Windows 11 Obsidian

同期は双方向です。iPhoneで書いたノートはWindowsに、Windowsで書いたノートはiPhoneに反映されます。WebDAVサーバは自宅LAN内に閉じており、外出先からはVPN経由でアクセスします。


Step 1: サーバ環境構築

Nginx + WebDAVモジュールのインストール

Obsidianの Remotely Save プラグインはWebDAVのLOCK/UNLOCKメソッドを使うため、通常のnginxパッケージでは動きません。nginx-extras をインストールすると、LOCK/UNLOCK対応の dav_ext モジュールが含まれます。

sudo apt update
sudo apt install nginx nginx-extras

Vault用ディレクトリの作成

Obsidianの保管庫(Vault)を格納するディレクトリを作成します。Nginx(www-dataユーザー)が書き込めるよう、権限を設定しておきます。

mkdir -p /home/<USER>/obsidian-vault
sudo chown <USER>:www-data /home/<USER>/obsidian-vault
sudo chmod 775 /home/<USER>/obsidian-vault
chmod o+x /home/<USER>

最後の chmod o+x は、www-dataユーザーがホームディレクトリを通過してVaultディレクトリにアクセスするために必要です。これを忘れると404エラーになります。

Basic認証の設定

WebDAVへの不正アクセスを防ぐため、Basic認証を設定します。

sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd <USER>

パスワードを聞かれるので、設定してください。

ここまでで、Nginx + WebDAVの基盤が整いました。次はSSL証明書を準備します。


Step 2: SSL証明書の作成(mkcert)

自宅LAN内のプライベートIPに対してLet's Encryptは使えません。そこで、mkcertを使ってローカルCA(認証局)と証明書を生成します。

Windows11でCA作成と証明書生成

mkcertのインストールとCA作成は、普段使っているWindows PCで行います。

winget install FiloSottile.mkcert

インストール後、PowerShellを再起動してください。その後、ローカルCAを作成し、サーバ用の証明書と秘密鍵を生成します。

mkcert -install
mkcert <SERVER_IP> <HOSTNAME>

<SERVER_IP>+1.pem(証明書)と <SERVER_IP>+1-key.pem(秘密鍵)が生成されます。これをサーバに転送します。

scp <SERVER_IP>+1.pem <USER>@<SERVER_IP>:~/
scp <SERVER_IP>+1-key.pem <USER>@<SERVER_IP>:~/

サーバで証明書を配置

転送した証明書と秘密鍵を、Nginxが読める場所に配置します。秘密鍵はwww-dataグループのみ読めるよう、権限を絞っておきます。

sudo mkdir -p /etc/ssl/local
sudo mv ~/<SERVER_IP>+1.pem /etc/ssl/local/cert.pem
sudo mv ~/<SERVER_IP>+1-key.pem /etc/ssl/local/key.pem
sudo chmod 644 /etc/ssl/local/cert.pem
sudo chmod 640 /etc/ssl/local/key.pem
sudo chown root:www-data /etc/ssl/local/key.pem

iPhoneにCA証明書をインストール

iPhoneでHTTPS接続を信頼させるため、mkcertのルートCA証明書をインストールします。Windows上の C:\Users\<USER>\AppData\Local\mkcert\rootCA.pem をメールで自分宛に送信し、iPhoneで以下の手順で設定します。

  1. メールで rootCA.pem を開く → プロファイルがダウンロードされる
  2. 設定 → 一般 → VPNとデバイス管理 → プロファイルをインストール
  3. 設定 → 一般 → 情報 → 証明書信頼設定 → mkcertのCAを有効にする

3番目のステップを忘れると、証明書がインストールされていても信頼されません。必ず有効化してください。

ここまでで、SSL証明書の準備が完了しました。次はNginxにWebDAVの設定を入れます。


Step 3: Nginx WebDAV設定

NginxにHTTPS + WebDAV + Basic認証 + CORS の設定を追加します。以下の内容で /etc/nginx/sites-available/webdav を作成してください。

server {
    listen 443 ssl;
    server_name <SERVER_IP>;

    ssl_certificate     /etc/ssl/local/cert.pem;
    ssl_certificate_key /etc/ssl/local/key.pem;

    access_log /var/log/nginx/access.log;

    location /obsidian/ {
        alias /home/<USER>/obsidian-vault/;

        dav_methods     PUT DELETE MKCOL COPY MOVE;
        dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
        dav_access      user:rw group:r all:r;

        client_max_body_size 50M;
        create_full_put_path on;

        auth_basic "Obsidian WebDAV";
        auth_basic_user_file /etc/nginx/.htpasswd;

        # CORS設定(iOS対応で最も重要な部分)
        set $cors_origin $http_origin;
        if ($cors_origin = '') {
            set $cors_origin '*';
        }

        add_header 'Access-Control-Allow-Origin' $cors_origin always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, MKCOL, COPY, MOVE, PROPFIND, OPTIONS, LOCK, UNLOCK' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Depth, Overwrite, Destination, Lock-Token, Timeout, If, Cache-Control' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;

        if ($request_method = OPTIONS) {
            return 204;
        }
    }
}

設定を有効化して、Nginxを再起動します。

sudo ln -s /etc/nginx/sites-available/webdav /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

CORS設定の解説

後述するiOS修正で fetch() APIを使いますが、fetch() はCORSを強制します(Obsidianの requestUrl() はCORSをバイパスします)。iOSのObsidianはCapacitor WebView内で動作するため、Originは capacitor://localhost になります。

Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true はHTTP仕様上、同時に使えません。そのため、リクエストのOriginヘッダーを動的に返す設定にしています。

動作確認

curlでWebDAVの動作を確認します。XMLのレスポンスが返ってくれば成功です。

curl -k -u <USER> https://<SERVER_IP>/obsidian/ -X PROPFIND

ここまでで、WebDAVサーバが動作する状態になりました。次はWindows版Obsidianから接続します。


Step 4: Windows版 Obsidian の設定

Obsidianのインストールと保管庫の作成

obsidian.md からWindows版をインストールし、起動時に保管庫(Vault)を作成します。

Remotely Saveプラグインの導入

Obsidianの設定から、コミュニティプラグイン「Remotely Save」をインストールします。

  1. 設定 → コミュニティプラグイン → 制限モードを解除 → 閲覧
  2. 「Remotely Save」を検索 → インストール → 有効化
  3. プラグイン設定で以下を入力します。
    • リモートサービス: WebDAV
    • Server Address: https://<SERVER_IP>/obsidian/
    • Username: htpasswdで設定したユーザー名
    • Password: htpasswdで設定したパスワード
  4. Check → 接続成功を確認します
  5. 同期アイコン(円形矢印)をクリックして初回同期を実行します

ハマりポイント: SSL検証

mkcertの自己署名証明書を使っている場合、Windows版ObsidianのRemotely Saveは問題なく動作します。しかし、Obsidian Gitプラグインなどgitコマンドを内部で使うプラグインでは、SSL検証エラーが出る場合があります。その場合は以下で回避できます。

cd C:\Users\<USER>\obsidian-vault
git config http.sslVerify false

ここまでで、Windows PCからWebDAV経由のObsidian同期が動作しています。次はiPhoneですが、ここからが本記事の本題です。


Step 5: iOS版で発生した問題

iPhoneのObsidianにRemotely Saveをインストールし、Windowsと同じWebDAV設定を入れてCheckを押すと、以下のエラーが表示されました。

Error: Request failed. The Internet connection appears to be offline.
The webdav server cannot be reached

サーバ側のNginxアクセスログを tail -f で監視しても、リクエストが一切到達しません

しかし、iPhoneのSafariで同じURL(https://<SERVER_IP>/obsidian/)にアクセスすると、証明書エラーなしで表示されます(403 Forbiddenはディレクトリ一覧が無いだけで正常)。

つまり、iOSはmkcertのCA証明書を信頼しているが、Obsidianアプリ内部のHTTPクライアントは信頼していない、という状況です。

試したこと(全て失敗)

iOS版の同期方法として、WebDAV以外にもいくつか試しましたが、全て失敗しました。

  • Obsidian Gitプラグイン(iOS版) → isomorphic-gitベースで不安定、clone失敗
  • Working Copy(iOS Gitクライアント) → 接続エラー
  • GitSync(ネイティブGitクライアント) → Giteaトークン認証問題
  • HTTP(非HTTPS)でのWebDAV接続 → iOSのApp Transport Security (ATS) でブロック

つまり、iOS版Obsidianで自宅サーバに接続する方法は、既存の手段では全て行き詰まりました。ここからソースコード解析に入ります。


Step 6: 原因の特定

Remotely Saveのソースコード(TypeScript約3万行)をClaude Codeで解析しました。

リポジトリ: https://github.com/remotely-save/remotely-save

WebDAV通信の仕組み

src/fsWebdav.ts にWebDAV通信のコアがあります。WebDAVライブラリのHTTPリクエストを「パッチャー」でインターセプトし、Obsidianの requestUrl() APIにリダイレクトしています。

// src/fsWebdav.ts(概要)
getPatcher().patch("request", async (options) => {
  // ... ヘッダー変換 ...
  const resp = await requestUrl({ url: options.url, method: options.method, ... });
  // ... レスポンス変換 ...
});

問題の核心

Obsidianの requestUrl() はプラットフォームごとに異なるHTTPスタックを使います。問題はiOSだけ、ユーザーがインストールしたCA証明書を信頼しないことです。

プラットフォーム HTTPスタック ユーザーCA信頼
Windows/Mac (Electron) Node.js / Chromium
Android ネイティブHTTP
iOS ネイティブHTTP

一方、WKWebViewの fetch() APIはiOSシステム証明書ストア全体を参照するため、ユーザーCAも信頼します。Safariで動くのはこのためです。

SSL関連コードの調査

ソースコード全体を検索しましたが、rejectUnauthorizedNODE_TLS_REJECT_UNAUTHORIZED、証明書ピンニングなどの明示的なSSL設定は一切ありませんでした。SSL検証は完全にプラットフォーム依存です。つまり、プラグインの設定や環境変数では解決できません。

原因が特定できました。iOSの requestUrl() を迂回して、fetch() を使えば動くはずです。修正に進みます。


Step 7: ソースコードの修正

修正方針

src/fsWebdav.ts のパッチャー内に、iOSの場合のみ fetch() にフォールバックする処理を追加します。iOS以外のプラットフォームには一切影響を与えません。

修正内容

パッチャーコールバック内のヘッダー変換後に、以下のiOS専用分岐を追加しました。

if (Platform.isIosApp) {
  const fetchOptions: RequestInit = {
    method: options.method,
    headers: transformedHeaders,
  };
  if (
    options.data !== undefined &&
    options.data !== null &&
    options.method.toUpperCase() !== "GET" &&
    options.method.toUpperCase() !== "HEAD"
  ) {
    fetchOptions.body = options.data as string | ArrayBuffer;
  }

  let resp = await fetch(options.url, fetchOptions);

  // 既存のiOS向けPROPFIND 401ワークアラウンドを移植
  if (
    resp.status === 401 &&
    !options.url.endsWith("/") &&
    !options.url.endsWith(".md") &&
    options.method.toUpperCase() === "PROPFIND"
  ) {
    resp = await fetch(`${options.url}/`, fetchOptions);
  }

  return resp;
}
// iOS以外は従来通り requestUrl() を使用

修正のポイントは以下の通りです。

  • iOS以外には一切影響しないPlatform.isIosApptrue の場合のみ実行されます
  • fetch()Response オブジェクトを返すので、手動のレスポンスラッピングが不要です
  • オリジナルコードにあったiOS向けPROPFIND 401ワークアラウンドも fetch() パスに移植済みです
  • WebDAVの全メソッド(PROPFIND, MKCOL, PUT, GET, DELETE, LOCK, UNLOCK)は fetch() の標準仕様でサポートされています

ビルド修正

esbuild.config.mjs にも修正が必要でした。

  • target: "es2016""es2020"(依存パッケージのp-queue v8がBigIntリテラルを使用するため)
  • node:url プロトコルimportのshimプラグイン追加

ビルド実行

以下のコマンドで修正版の main.js を生成します。

npm install
npm run build2

約5MBの main.js が生成されます。

修正版プラグインのビルドが完了しました。次はこれをiPhoneに入れます。ここにもう一つハマりポイントがあります。


Step 8: 修正版プラグインのiPhoneへのデプロイ

修正版の main.js をiPhoneのObsidianプラグインフォルダに配置する必要があります。パスは以下の通りです。

このiPhone内/Obsidian/obsidian-vault/.obsidian/plugins/remotely-save/main.js

.obsidianフォルダが見えない問題

しかし、iOSでは以下の制約があり、通常の方法ではファイルを配置できません。

  • iOSの**「ファイル」アプリ**では、ドット始まりの隠しフォルダ(.obsidian)が表示されない
  • Apple Devices(iTunes) のファイル共有でも .obsidian フォルダにアクセスできない場合がある
  • Documents by Readdle でも、サンドボックス制限で他アプリのフォルダにコピーできない

解決方法: Taioアプリ

Taio(iOS用テキストエディタ/ファイルマネージャ、無料)は、隠しフォルダを含むファイル操作が可能でした。

手順は以下の通りです。

  1. 修正版 main.js をzipに圧縮し、WebサーバまたはAirDrop経由でiPhoneに転送します
  2. Taioのファイルブラウザで このiPhone内 → Obsidian → obsidian-vault → .obsidian → plugins → remotely-save に移動します
  3. main.js を上書きコピーします
  4. Obsidianを完全終了(タスクキル)して再起動します

修正版プラグインがiPhoneに入りました。いよいよ動作確認です。


Step 9: 動作確認

iPhoneのObsidianでRemotely Saveの設定を開き、「Check」を押します。

結果: Successful!

Nginxのアクセスログにも、iPhoneからのリクエストが正しく到達していることを確認できました。

"PROPFIND /obsidian/obsidian-vault/ HTTP/1.1" 207 "capacitor://localhost"
"MKCOL /obsidian/obsidian-vault/rs-test-folder-.../ HTTP/1.1" 201 "capacitor://localhost"
"PUT /obsidian/obsidian-vault/.../rs-test-file-... HTTP/1.1" 201 "capacitor://localhost"
"GET /obsidian/obsidian-vault/.../rs-test-file-... HTTP/1.1" 200 "capacitor://localhost"
"DELETE /obsidian/obsidian-vault/.../rs-test-file-... HTTP/1.1" 204 "capacitor://localhost"

PROPFIND, MKCOL, PUT, GET, DELETE — 全てのWebDAVオペレーションが成功しています。Originが capacitor://localhost であることも確認できます。

その後、実際にiPhoneで新しいノートを作成し、Windows PC側で同期を実行したところ、双方向でノートが同期されることを確認しました。


まとめ

解決に必要だった3つのこと

  1. Remotely Saveのソースコード修正 — iOSで requestUrl() の代わりに fetch() を使うフォールバック
  2. NginxのCORS設定 — Originが capacitor://localhost であることへの対応
  3. Taioアプリ — iOSの .obsidian 隠しフォルダにアクセスして main.js を差し替え

得られた教訓

  • iOSの requestUrl()(Obsidianネイティブ)はユーザーインストールのCA証明書を信頼しません。これはObsidian側の制約であり、プラグインの設定では回避できません
  • WKWebViewの fetch() はシステム証明書ストア全体を参照するので、同じiOSでもAPIによって挙動が異なります
  • fetch() を使う場合はCORSが強制されます。requestUrl() はCORSをバイパスするので、この差異を意識する必要があります
  • iOSの隠しフォルダアクセスは「ファイル」アプリでは不可能です。Taioのようなサードパーティアプリが必要です

GitHub

修正コードはForkとして公開しています。また、本家リポジトリにBug Reportを投稿しました。同じ問題で困っている方の参考になれば幸いです。


I am prioritizing self-hosted WebDAV with private CA for data sovereignty and privacy, moving away from public cloud services.

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?